gruff 0.14.0-java → 0.17.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/.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 +17 -16
  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/line.rb CHANGED
@@ -25,7 +25,7 @@ class Gruff::Line < Gruff::Base
25
25
  attr_writer :line_width
26
26
  attr_writer :dot_radius
27
27
 
28
- # default is +'circle'+, other options include square.
28
+ # default is +'circle'+, other options include +square+ and +diamond+.
29
29
  attr_writer :dot_style
30
30
 
31
31
  # Hide parts of the graph to fit more data points, or for a different appearance.
@@ -40,6 +40,23 @@ class Gruff::Line < Gruff::Base
40
40
  # The number of vertical lines shown.
41
41
  attr_writer :marker_x_count
42
42
 
43
+ # Call with target pixel width of graph (+800+, +400+, +300+), and/or +false+ to omit lines (points only).
44
+ #
45
+ # g = Gruff::Line.new(400) # 400px wide with lines
46
+ # g = Gruff::Line.new(400, false) # 400px wide, no lines (for backwards compatibility)
47
+ # g = Gruff::Line.new(false) # Defaults to 800px wide, no lines (for backwards compatibility)
48
+ #
49
+ # The preferred way is to call {#hide_dots=} or {#hide_lines=} instead.
50
+ def initialize(*args)
51
+ raise ArgumentError, 'Wrong number of arguments' if args.length > 2
52
+
53
+ if args.empty? || (!args.first.is_a?(Numeric) && !args.first.is_a?(String))
54
+ super()
55
+ else
56
+ super args.shift
57
+ end
58
+ end
59
+
43
60
  # Get the value if somebody has defined it.
44
61
  def baseline_value
45
62
  if @reference_lines.key?(:baseline)
@@ -64,23 +81,6 @@ class Gruff::Line < Gruff::Base
64
81
  @reference_lines[:baseline][:color] = new_value
65
82
  end
66
83
 
67
- # Call with target pixel width of graph (+800+, +400+, +300+), and/or +false+ to omit lines (points only).
68
- #
69
- # g = Gruff::Line.new(400) # 400px wide with lines
70
- # g = Gruff::Line.new(400, false) # 400px wide, no lines (for backwards compatibility)
71
- # g = Gruff::Line.new(false) # Defaults to 800px wide, no lines (for backwards compatibility)
72
- #
73
- # The preferred way is to call {#hide_dots=} or {#hide_lines=} instead.
74
- def initialize(*args)
75
- raise ArgumentError, 'Wrong number of arguments' if args.length > 2
76
-
77
- if args.empty? || (!args.first.is_a?(Numeric) && !args.first.is_a?(String))
78
- super()
79
- else
80
- super args.shift
81
- end
82
- end
83
-
84
84
  # This method allows one to plot a dataset with both X and Y data.
85
85
  #
86
86
  # @overload dataxy(name, x_data_points = [], y_data_points = [], color = nil)
@@ -118,12 +118,14 @@ class Gruff::Line < Gruff::Base
118
118
  def dataxy(name, x_data_points = [], y_data_points = [], color = nil)
119
119
  # make sure it's an array
120
120
  x_data_points = Array(x_data_points)
121
- y_data_points = Array(y_data_points)
122
121
 
123
122
  raise ArgumentError, 'x_data_points is nil!' if x_data_points.empty?
124
123
 
125
124
  if x_data_points.all? { |p| p.is_a?(Array) && p.size == 2 }
125
+ color = y_data_points if y_data_points.is_a?(String)
126
126
  x_data_points, y_data_points = x_data_points.transpose
127
+ else
128
+ y_data_points = Array(y_data_points)
127
129
  end
128
130
 
129
131
  raise ArgumentError, 'x_data_points.length != y_data_points.length!' if x_data_points.length != y_data_points.length
@@ -163,7 +165,7 @@ private
163
165
  end
164
166
 
165
167
  def draw_horizontal_reference_line(reference_line)
166
- level = @graph_top + (@graph_height - reference_line[:norm_value] * @graph_height)
168
+ level = @graph_top + (@graph_height - (reference_line[:norm_value] * @graph_height))
167
169
  draw_reference_line(reference_line, @graph_left, @graph_left + @graph_width, level, level)
168
170
  end
169
171
 
@@ -174,7 +176,7 @@ private
174
176
 
175
177
  def draw_graph
176
178
  # Check to see if more than one datapoint was given. NaN can result otherwise.
177
- @x_increment = (column_count > 1) ? (@graph_width / (column_count - 1).to_f) : @graph_width
179
+ @x_increment = column_count > 1 ? @graph_width / (column_count - 1) : @graph_width
178
180
 
179
181
  @reference_lines.each_value do |curr_reference_line|
180
182
  draw_horizontal_reference_line(curr_reference_line) if curr_reference_line.key?(:norm_value)
@@ -187,22 +189,22 @@ private
187
189
  one_point = contains_one_point_only?(data_row)
188
190
 
189
191
  data_row.coordinates.each_with_index do |(x_data, y_data), index|
190
- if x_data.nil?
191
- # use the old method: equally spaced points along the x-axis
192
- new_x = @graph_left + (@x_increment * index)
193
- draw_label(new_x, index)
194
- else
195
- new_x = get_x_coord(x_data, @graph_width, @graph_left)
196
- @labels.each do |label_pos, _|
197
- draw_label(@graph_left + ((label_pos - @minimum_x_value) * @graph_width) / (@maximum_x_value - @minimum_x_value), label_pos)
192
+ new_x = begin
193
+ if x_data.nil?
194
+ # use the old method: equally spaced points along the x-axis
195
+ @graph_left + (@x_increment * index)
196
+ else
197
+ get_x_coord(x_data, @graph_width, @graph_left)
198
198
  end
199
199
  end
200
+ draw_label_for_x_data(x_data, new_x, index)
201
+
200
202
  unless y_data # we can't draw a line for a null data point, we can still label the axis though
201
203
  prev_x = prev_y = nil
202
204
  next
203
205
  end
204
206
 
205
- new_y = @graph_top + (@graph_height - y_data * @graph_height)
207
+ new_y = @graph_top + (@graph_height - (y_data * @graph_height))
206
208
 
207
209
  # Reset each time to avoid thin-line errors
208
210
  stroke_width = @line_width || clip_value_if_greater_than(@columns / (store.norm_data.first.y_points.size * 4), 5.0)
@@ -225,12 +227,12 @@ private
225
227
 
226
228
  def setup_data
227
229
  # Update the global min/max values for the x data
228
- @maximum_x_value ||= store.max_x
229
- @minimum_x_value ||= store.min_x
230
+ @maximum_x_value = (@maximum_x_value || store.max_x).to_f
231
+ @minimum_x_value = (@minimum_x_value || store.min_x).to_f
230
232
 
231
233
  # Deal with horizontal reference line values that exceed the existing minimum & maximum values.
232
- possible_maximums = [maximum_value.to_f]
233
- possible_minimums = [minimum_value.to_f]
234
+ possible_maximums = [maximum_value]
235
+ possible_minimums = [minimum_value]
234
236
 
235
237
  @reference_lines.each_value do |curr_reference_line|
236
238
  if curr_reference_line.key?(:value)
@@ -253,41 +255,40 @@ private
253
255
  def normalize
254
256
  return unless data_given?
255
257
 
256
- spread_x = @maximum_x_value.to_f - @minimum_x_value.to_f
258
+ spread_x = @maximum_x_value - @minimum_x_value
257
259
  store.normalize(minimum_x: @minimum_x_value, spread_x: spread_x, minimum_y: minimum_value, spread_y: @spread)
258
260
 
259
261
  @reference_lines.each_value do |curr_reference_line|
260
262
  # We only care about horizontal markers ... for normalization.
261
263
  # Vertical markers won't have a :value, they will have an :index
262
264
 
263
- curr_reference_line[:norm_value] = ((curr_reference_line[:value].to_f - minimum_value) / @spread.to_f) if curr_reference_line.key?(:value)
265
+ curr_reference_line[:norm_value] = ((curr_reference_line[:value].to_f - minimum_value) / @spread) if curr_reference_line.key?(:value)
264
266
  end
265
267
  end
266
268
 
267
- def sort_norm_data
268
- super unless store.data.any?(&:x_points)
269
- end
270
-
271
269
  def draw_line_markers
272
270
  # do all of the stuff for the horizontal lines on the y-axis
273
271
  super
274
272
  return if @hide_line_markers
273
+ return unless @show_vertical_markers
275
274
 
276
275
  (0..@marker_x_count).each do |index|
277
- if @show_vertical_markers
278
- x = @graph_left + @graph_width - index * @graph_width / @marker_x_count
276
+ draw_marker_vertical_line(@graph_left + @graph_width - (index * @graph_width / @marker_x_count))
277
+ end
278
+ end
279
279
 
280
- Gruff::Renderer::Line.new(renderer, color: @marker_color).render(x, @graph_bottom, x, @graph_top)
281
- # If the user specified a marker shadow color, draw a shadow just below it
282
- if @marker_shadow_color
283
- Gruff::Renderer::Line.new(renderer, color: @marker_shadow_color).render(x + 1, @graph_bottom, x + 1, @graph_top)
284
- end
280
+ def draw_label_for_x_data(x_data, new_x, index)
281
+ if x_data.nil?
282
+ draw_label(new_x, index)
283
+ else
284
+ @labels.each do |label_pos, _|
285
+ draw_label(@graph_left + (((label_pos - @minimum_x_value) * @graph_width) / (@maximum_x_value - @minimum_x_value)), label_pos)
285
286
  end
286
287
  end
287
288
  end
288
289
 
289
290
  def get_x_coord(x_data_point, width, offset)
290
- x_data_point * width + offset
291
+ (x_data_point * width) + offset
291
292
  end
292
293
 
293
294
  def contains_one_point_only?(data_row)
@@ -21,7 +21,7 @@ module Gruff
21
21
 
22
22
  @legend_labels = store.data.map(&:label)
23
23
 
24
- legend_height = scale_fontsize(store.length * calculate_line_height + @top_margin + @bottom_margin)
24
+ legend_height = scale((store.length * calculate_line_height) + @top_margin + @bottom_margin)
25
25
 
26
26
  @original_rows = @raw_rows
27
27
  @original_columns = @raw_columns
@@ -32,7 +32,7 @@ module Gruff
32
32
  @columns += calculate_legend_width + @left_margin
33
33
  else
34
34
  font = @legend_font.dup
35
- font.size = scale_fontsize(font.size)
35
+ font.size = scale(font.size)
36
36
  @rows += store.length * calculate_caps_height(font) * 1.7
37
37
  end
38
38
 
@@ -45,7 +45,7 @@ module Gruff
45
45
 
46
46
  def calculate_legend_width
47
47
  width = @legend_labels.map { |label| calculate_width(@legend_font, label) }.max
48
- scale_fontsize(width + 40 * 1.7)
48
+ scale(width + (40 * 1.7))
49
49
  end
50
50
 
51
51
  ##
@@ -69,17 +69,17 @@ module Gruff
69
69
 
70
70
  @legend_labels.each_with_index do |legend_label, index|
71
71
  # Draw label
72
- label = truncate_legend_label(legend_label)
73
- text_renderer = Gruff::Renderer::Text.new(renderer, label, font: @legend_font)
74
72
  x_offset = current_x_offset + (legend_square_width * 1.7)
73
+ label = truncate_legend_label(legend_label, x_offset)
74
+ text_renderer = Gruff::Renderer::Text.new(renderer, label, font: @legend_font)
75
75
  text_renderer.add_to_render_queue(@raw_columns, 1.0, x_offset, current_y_offset, Magick::WestGravity)
76
76
 
77
77
  # Now draw box with color of this dataset
78
78
  rect_renderer = Gruff::Renderer::Rectangle.new(renderer, color: store.data[index].color)
79
79
  rect_renderer.render(current_x_offset,
80
- current_y_offset - legend_square_width / 2.0,
80
+ current_y_offset - (legend_square_width / 2.0),
81
81
  current_x_offset + legend_square_width,
82
- current_y_offset + legend_square_width / 2.0)
82
+ current_y_offset + (legend_square_width / 2.0))
83
83
 
84
84
  current_y_offset += calculate_line_height
85
85
  end
@@ -90,13 +90,13 @@ module Gruff
90
90
  #
91
91
  # Department of Hu...
92
92
 
93
- def truncate_legend_label(label)
93
+ def truncate_legend_label(label, x_offset)
94
94
  truncated_label = label.to_s
95
95
 
96
96
  font = @legend_font.dup
97
- font.size = scale_fontsize(font.size)
98
- max_width = @columns - @legend_left_margin - @right_margin
99
- while calculate_width(font, truncated_label) > max_width && truncated_label.length > 1
97
+ font.size = scale(font.size)
98
+ max_width = @columns - scale(x_offset) - @right_margin
99
+ while calculate_width(font, "#{truncated_label}...") > max_width && truncated_label.length > 1
100
100
  truncated_label = truncated_label[0..truncated_label.length - 2]
101
101
  end
102
102
  truncated_label + (truncated_label.length < label.to_s.length ? '...' : '')
data/lib/gruff/net.rb CHANGED
@@ -43,6 +43,21 @@ private
43
43
  @marker_font.bold = true
44
44
  end
45
45
 
46
+ def setup_drawing
47
+ @center_labels_over_point = false
48
+ super
49
+ end
50
+
51
+ def setup_graph_measurements
52
+ super
53
+
54
+ @radius = @graph_height / 2.0
55
+ @circle_radius = @dot_radius || clip_value_if_greater_than(@columns / (store.norm_data.first.points.size * 2.5), 5.0)
56
+ @stroke_width = @line_width || clip_value_if_greater_than(@columns / (store.norm_data.first.points.size * 4.0), 5.0)
57
+ @center_x = @graph_left + (@graph_width / 2.0)
58
+ @center_y = @graph_top + (@graph_height / 2.0) + 10
59
+ end
60
+
46
61
  def draw_graph
47
62
  store.norm_data.each do |data_row|
48
63
  data_row.points.each_with_index do |data_point, index|
@@ -50,15 +65,15 @@ private
50
65
 
51
66
  rad_pos = index * Math::PI * 2 / column_count
52
67
  point_distance = data_point * @radius
53
- start_x = @center_x + Math.sin(rad_pos) * point_distance
54
- start_y = @center_y - Math.cos(rad_pos) * point_distance
68
+ start_x = @center_x + (Math.sin(rad_pos) * point_distance)
69
+ start_y = @center_y - (Math.cos(rad_pos) * point_distance)
55
70
 
56
71
  next_index = index + 1 < data_row.points.length ? index + 1 : 0
57
72
 
58
73
  next_rad_pos = next_index * Math::PI * 2 / column_count
59
74
  next_point_distance = data_row.points[next_index] * @radius
60
- end_x = @center_x + Math.sin(next_rad_pos) * next_point_distance
61
- end_y = @center_y - Math.cos(next_rad_pos) * next_point_distance
75
+ end_x = @center_x + (Math.sin(next_rad_pos) * next_point_distance)
76
+ end_y = @center_y - (Math.cos(next_rad_pos) * next_point_distance)
62
77
 
63
78
  Gruff::Renderer::Line.new(renderer, color: data_row.color, width: @stroke_width).render(start_x, start_y, end_x, end_y)
64
79
 
@@ -70,16 +85,6 @@ private
70
85
  end
71
86
  end
72
87
 
73
- def setup_drawing
74
- super
75
-
76
- @radius = @graph_height / 2.0
77
- @circle_radius = @dot_radius || clip_value_if_greater_than(@columns / (store.norm_data.first.points.size * 2.5), 5.0)
78
- @stroke_width = @line_width || clip_value_if_greater_than(@columns / (store.norm_data.first.points.size * 4), 5.0)
79
- @center_x = @graph_left + (@graph_width / 2.0)
80
- @center_y = @graph_top + (@graph_height / 2.0) + 10
81
- end
82
-
83
88
  # the lines connecting in the center, with the first line vertical
84
89
  def draw_line_markers
85
90
  return if @hide_line_markers
@@ -89,7 +94,7 @@ private
89
94
  rad_pos = index * Math::PI * 2 / column_count
90
95
 
91
96
  Gruff::Renderer::Line.new(renderer, color: @marker_color)
92
- .render(@center_x, @center_y, @center_x + Math.sin(rad_pos) * @radius, @center_y - Math.cos(rad_pos) * @radius)
97
+ .render(@center_x, @center_y, @center_x + (Math.sin(rad_pos) * @radius), @center_y - (Math.cos(rad_pos) * @radius))
93
98
 
94
99
  marker_label = @labels[index] ? @labels[index].to_s : '000'
95
100
  draw_label(@center_x, @center_y, rad_pos * 360 / (2 * Math::PI), @radius + @circle_radius, marker_label)
@@ -99,9 +104,9 @@ private
99
104
  def draw_label(center_x, center_y, angle, radius, amount)
100
105
  x_offset = center_x # + 15 # The label points need to be tweaked slightly
101
106
  y_offset = center_y # + 0 # This one doesn't though
102
- x = x_offset + (radius + LABEL_MARGIN) * Math.sin(deg2rad(angle))
103
- y = y_offset - (radius + LABEL_MARGIN) * Math.cos(deg2rad(angle))
107
+ x = x_offset + ((radius + LABEL_MARGIN) * Math.sin(deg2rad(angle)))
108
+ y = y_offset - ((radius + LABEL_MARGIN) * Math.cos(deg2rad(angle)))
104
109
 
105
- draw_label_at(1.0, 1.0, x, y, amount, Magick::CenterGravity)
110
+ draw_label_at(1.0, 1.0, x, y, amount, gravity: Magick::CenterGravity)
106
111
  end
107
112
  end
@@ -23,7 +23,6 @@ module Magick
23
23
  def fill=(fill)
24
24
  fill = { white: '#FFFFFF' }[fill.to_sym] || fill
25
25
  @draw.fill = Magick4J.ColorDatabase.query_default(fill)
26
- self
27
26
  end
28
27
  end
29
28
  end
@@ -3,6 +3,7 @@
3
3
  # @private
4
4
  module String::GruffCommify
5
5
  THOUSAND_SEPARATOR = ','
6
+ private_constant :THOUSAND_SEPARATOR
6
7
 
7
8
  refine String do
8
9
  # Taken from http://codesnippets.joyent.com/posts/show/330
data/lib/gruff/pie.rb CHANGED
@@ -9,14 +9,14 @@
9
9
  # g.data 'Hamburgers', 50
10
10
  # g.write("pie_keynote.png")
11
11
  #
12
- # To control where the pie chart starts creating slices, use {#zero_degree=}.
12
+ # To control where the pie chart starts creating slices, use {#start_degree=}.
13
13
  #
14
14
  class Gruff::Pie < Gruff::Base
15
- DEFAULT_TEXT_OFFSET_PERCENTAGE = 0.15
15
+ DEFAULT_TEXT_OFFSET_PERCENTAGE = 0.1
16
16
 
17
17
  # Can be used to make the pie start cutting slices at the top (-90.0)
18
- # or at another angle. Default is +0.0+, which starts at 3 o'clock.
19
- attr_writer :zero_degree
18
+ # or at another angle. Default is +-90.0+, which starts at 3 o'clock.
19
+ attr_writer :start_degree
20
20
 
21
21
  # Set the number output format lambda.
22
22
  attr_writer :label_formatting
@@ -26,25 +26,34 @@ class Gruff::Pie < Gruff::Base
26
26
  attr_writer :hide_labels_less_than
27
27
 
28
28
  # Affect the distance between the percentages and the pie chart.
29
- # Defaults to +0.15+.
29
+ # Defaults to +0.1+.
30
30
  attr_writer :text_offset_percentage
31
31
 
32
32
  ## Use values instead of percentages.
33
33
  attr_writer :show_values_as_labels
34
34
 
35
- private
35
+ # Set to +true+ if you want the data sets sorted with largest avg values drawn
36
+ # first. Default is +true+.
37
+ attr_writer :sort
36
38
 
37
- def initialize_store
38
- @store = Gruff::Store.new(Gruff::Store::CustomData)
39
+ # Can be used to make the pie start cutting slices at the top (-90.0)
40
+ # or at another angle. Default is +-90.0+, which starts at 3 o'clock.
41
+ # @deprecated Please use +start_degree+ attribute instead.
42
+ def zero_degree=(value)
43
+ warn '#zero_degree= is deprecated. Please use `start_degree` attribute instead'
44
+ @start_degree = value
39
45
  end
40
46
 
47
+ private
48
+
41
49
  def initialize_attributes
42
50
  super
43
- @zero_degree = 0.0
51
+ @start_degree = -90.0
44
52
  @hide_labels_less_than = 0.0
45
53
  @text_offset_percentage = DEFAULT_TEXT_OFFSET_PERCENTAGE
46
54
  @show_values_as_labels = false
47
55
  @marker_font.bold = true
56
+ @sort = true
48
57
 
49
58
  @hide_line_markers = true
50
59
  @hide_line_markers.freeze
@@ -52,6 +61,11 @@ private
52
61
  @label_formatting = ->(value, percentage) { @show_values_as_labels ? value.to_s : "#{percentage}%" }
53
62
  end
54
63
 
64
+ def setup_drawing
65
+ @center_labels_over_point = false
66
+ super
67
+ end
68
+
55
69
  def draw_graph
56
70
  slices.each do |slice|
57
71
  if slice.value > 0
@@ -83,7 +97,7 @@ private
83
97
  # Spatial Value-Related Methods
84
98
 
85
99
  def chart_degrees
86
- @chart_degrees ||= @zero_degree
100
+ @chart_degrees ||= @start_degree
87
101
  end
88
102
 
89
103
  attr_reader :graph_height
@@ -127,12 +141,12 @@ private
127
141
  if slice.percentage >= @hide_labels_less_than
128
142
  x, y = label_coordinates_for slice
129
143
  label = @label_formatting.call(slice.value, slice.percentage)
130
- draw_label_at(1.0, 1.0, x, y, label, Magick::CenterGravity)
144
+ draw_label_at(1.0, 1.0, x, y, label, gravity: Magick::CenterGravity)
131
145
  end
132
146
  end
133
147
 
134
148
  def label_coordinates_for(slice)
135
- angle = chart_degrees + slice.degrees / 2
149
+ angle = chart_degrees + (slice.degrees / 2.0)
136
150
 
137
151
  [x_label_coordinate(angle), y_label_coordinate(angle)]
138
152
  end
@@ -3,17 +3,18 @@
3
3
  module Gruff
4
4
  # @private
5
5
  class Renderer::DashLine
6
- def initialize(renderer, color:, width:)
6
+ def initialize(renderer, color:, width:, dasharray: [10, 20])
7
7
  @renderer = renderer
8
8
  @color = color
9
9
  @width = width
10
+ @dasharray = dasharray
10
11
  end
11
12
 
12
13
  def render(start_x, start_y, end_x, end_y)
13
14
  @renderer.draw.push
14
15
  @renderer.draw.stroke_color(@color)
15
16
  @renderer.draw.fill_opacity(0.0)
16
- @renderer.draw.stroke_dasharray(10, 20)
17
+ @renderer.draw.stroke_dasharray(*@dasharray)
17
18
  @renderer.draw.stroke_width(@width)
18
19
  @renderer.draw.line(start_x, start_y, end_x, end_y)
19
20
  @renderer.draw.pop
@@ -3,37 +3,50 @@
3
3
  module Gruff
4
4
  # @private
5
5
  class Renderer::Dot
6
- def initialize(renderer, style, color:, width: 1.0)
6
+ def initialize(renderer, style, color:, width: 1.0, opacity: 1.0)
7
7
  @renderer = renderer
8
8
  @style = style
9
9
  @color = color
10
10
  @width = width
11
+ @opacity = opacity
11
12
  end
12
13
 
13
- def render(new_x, new_y, circle_radius)
14
- # @renderer.draw.push # TODO
14
+ def render(new_x, new_y, radius)
15
+ @renderer.draw.push
15
16
  @renderer.draw.stroke_width(@width)
16
17
  @renderer.draw.stroke(@color)
17
18
  @renderer.draw.fill(@color)
18
- if @style.to_s == 'square'
19
- square(new_x, new_y, circle_radius)
19
+ @renderer.draw.fill_opacity(@opacity)
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)
20
25
  else
21
- circle(new_x, new_y, circle_radius)
26
+ circle(new_x, new_y, radius)
22
27
  end
23
- # @renderer.draw.pop # TODO
28
+ @renderer.draw.pop
24
29
  end
25
30
 
26
- def circle(new_x, new_y, circle_radius)
27
- @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)
28
33
  end
29
34
 
30
- def square(new_x, new_y, circle_radius)
31
- offset = (circle_radius * 0.8).to_i
32
- corner1 = new_x - offset
33
- corner2 = new_y - offset
34
- corner3 = new_x + offset
35
- 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
36
40
  @renderer.draw.rectangle(corner1, corner2, corner3, corner4)
37
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
38
51
  end
39
52
  end
@@ -5,16 +5,14 @@ module Gruff
5
5
  class Renderer::Line
6
6
  EPSILON = 0.001
7
7
 
8
- def initialize(renderer, color:, width: nil, shadow_color: nil)
8
+ def initialize(renderer, color:, width: nil)
9
9
  @renderer = renderer
10
10
  @color = color
11
11
  @width = width
12
- @shadow_color = shadow_color
13
12
  end
14
13
 
15
14
  def render(start_x, start_y, end_x, end_y)
16
15
  render_line(start_x, start_y, end_x, end_y, @color)
17
- render_line(start_x, start_y + 1, end_x, end_y + 1, @shadow_color) if @shadow_color
18
16
  end
19
17
 
20
18
  private
@@ -3,14 +3,18 @@
3
3
  module Gruff
4
4
  # @private
5
5
  class Renderer::Rectangle
6
- def initialize(renderer, color: nil)
6
+ def initialize(renderer, color: nil, width: 1.0, opacity: 1.0)
7
7
  @renderer = renderer
8
8
  @color = color
9
+ @width = width
10
+ @opacity = opacity
9
11
  end
10
12
 
11
13
  def render(upper_left_x, upper_left_y, lower_right_x, lower_right_y)
12
14
  @renderer.draw.push
13
- @renderer.draw.stroke('transparent')
15
+ @renderer.draw.stroke_width(@width)
16
+ @renderer.draw.stroke(@color) if @width > 1.0
17
+ @renderer.draw.fill_opacity(@opacity)
14
18
  @renderer.draw.fill(@color) if @color
15
19
  @renderer.draw.rectangle(upper_left_x, upper_left_y, lower_right_x, lower_right_y)
16
20
  @renderer.draw.pop
@@ -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
@@ -44,8 +40,8 @@ module Gruff
44
40
 
45
41
  # Make a new image at the current size with a solid +color+.
46
42
  def solid_background(columns, rows, color)
47
- Magick::Image.new(columns, rows) do
48
- self.background_color = color
43
+ Magick::Image.new(columns, rows) do |img|
44
+ img.background_color = color
49
45
  end
50
46
  end
51
47
 
@@ -97,8 +93,8 @@ module Gruff
97
93
 
98
94
  # Use with a theme to make a transparent background
99
95
  def render_transparent_background(columns, rows)
100
- Magick::Image.new(columns, rows) do
101
- self.background_color = 'transparent'
96
+ Magick::Image.new(columns, rows) do |img|
97
+ img.background_color = 'transparent'
102
98
  end
103
99
  end
104
100
  end
@@ -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