gruff 0.15.0-java → 0.18.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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +21 -5
- data/.gitignore +1 -0
- data/.rubocop.yml +0 -12
- data/CHANGELOG.md +52 -0
- data/README.md +14 -3
- data/gruff.gemspec +3 -4
- data/lib/gruff/accumulator_bar.rb +1 -1
- data/lib/gruff/area.rb +6 -4
- data/lib/gruff/bar.rb +53 -32
- data/lib/gruff/base.rb +297 -186
- data/lib/gruff/bezier.rb +4 -2
- data/lib/gruff/box.rb +180 -0
- data/lib/gruff/bubble.rb +99 -0
- data/lib/gruff/bullet.rb +5 -5
- data/lib/gruff/candlestick.rb +120 -0
- data/lib/gruff/dot.rb +11 -12
- data/lib/gruff/font.rb +3 -0
- data/lib/gruff/helper/bar_conversion.rb +6 -10
- data/lib/gruff/helper/bar_mixin.rb +25 -0
- data/lib/gruff/helper/bar_value_label.rb +24 -43
- data/lib/gruff/helper/stacked_mixin.rb +19 -1
- data/lib/gruff/histogram.rb +9 -6
- data/lib/gruff/line.rb +67 -43
- data/lib/gruff/mini/legend.rb +15 -11
- data/lib/gruff/net.rb +23 -18
- data/lib/gruff/patch/string.rb +1 -0
- data/lib/gruff/pie.rb +26 -12
- data/lib/gruff/renderer/circle.rb +3 -1
- data/lib/gruff/renderer/dash_line.rb +3 -2
- data/lib/gruff/renderer/dot.rb +28 -15
- data/lib/gruff/renderer/line.rb +1 -3
- data/lib/gruff/renderer/rectangle.rb +6 -2
- data/lib/gruff/renderer/renderer.rb +0 -4
- data/lib/gruff/renderer/text.rb +7 -1
- data/lib/gruff/scatter.rb +84 -81
- data/lib/gruff/side_bar.rb +64 -31
- data/lib/gruff/side_stacked_bar.rb +43 -55
- data/lib/gruff/spider.rb +52 -14
- data/lib/gruff/stacked_area.rb +18 -8
- data/lib/gruff/stacked_bar.rb +59 -29
- data/lib/gruff/store/xy_data.rb +8 -9
- data/lib/gruff/store/xy_pointsizes_data.rb +60 -0
- data/lib/gruff/version.rb +1 -1
- data/lib/gruff.rb +11 -12
- metadata +9 -6
- data/lib/gruff/scene.rb +0 -208
- data/lib/gruff/store/custom_data.rb +0 -36
@@ -16,7 +16,25 @@ module Gruff::Base::StackedMixin
|
|
16
16
|
|
17
17
|
max_hash.each_key do |key|
|
18
18
|
self.maximum_value = max_hash[key] if max_hash[key] > maximum_value
|
19
|
+
self.minimum_value = max_hash[key] if max_hash[key] < minimum_value
|
19
20
|
end
|
20
|
-
|
21
|
+
|
22
|
+
raise "Can't handle negative values in stacked graph" if minimum_value < 0
|
23
|
+
end
|
24
|
+
|
25
|
+
def normalized_stacked_bars
|
26
|
+
@normalized_stacked_bars ||= begin
|
27
|
+
stacked_bars = Array.new(column_count) { [] }
|
28
|
+
store.norm_data.each_with_index do |data_row, row_index|
|
29
|
+
data_row.points.each_with_index do |data_point, point_index|
|
30
|
+
stacked_bars[point_index] << BarData.new(data_point, store.data[row_index].points[point_index], data_row.color)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
stacked_bars
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# @private
|
38
|
+
class BarData < Struct.new(:point, :value, :color)
|
21
39
|
end
|
22
40
|
end
|
data/lib/gruff/histogram.rb
CHANGED
@@ -29,7 +29,7 @@ class Gruff::Histogram < Gruff::Bar
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def data(name, data_points = [], color = nil)
|
32
|
-
@data << [name, data_points, color]
|
32
|
+
@data << [name, Array(data_points), color]
|
33
33
|
end
|
34
34
|
|
35
35
|
private
|
@@ -40,15 +40,18 @@ private
|
|
40
40
|
@minimum_bin = nil
|
41
41
|
@maximum_bin = nil
|
42
42
|
end
|
43
|
-
private :initialize_attributes
|
44
43
|
|
45
44
|
def setup_data
|
46
45
|
@data.each do |(name, data_points, color)|
|
47
|
-
|
48
|
-
|
49
|
-
|
46
|
+
if data_points.empty?
|
47
|
+
store.add(name, [], color)
|
48
|
+
else
|
49
|
+
bins, freqs = HistogramArray.new(data_points).histogram(bin_width: @bin_width, min: @minimum_bin, max: @maximum_bin)
|
50
|
+
bins.each_with_index do |bin, index|
|
51
|
+
@labels[index] = bin
|
52
|
+
end
|
53
|
+
store.add(name, freqs, color)
|
50
54
|
end
|
51
|
-
store.add(name, freqs, color)
|
52
55
|
end
|
53
56
|
|
54
57
|
super
|
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,21 +81,28 @@ class Gruff::Line < Gruff::Base
|
|
64
81
|
@reference_lines[:baseline][:color] = new_value
|
65
82
|
end
|
66
83
|
|
67
|
-
#
|
84
|
+
# Input the data in the graph.
|
68
85
|
#
|
69
|
-
#
|
70
|
-
#
|
71
|
-
# g = Gruff::Line.new(false) # Defaults to 800px wide, no lines (for backwards compatibility)
|
86
|
+
# Parameters are an array where the first element is the name of the dataset
|
87
|
+
# and the value is an array of values to plot.
|
72
88
|
#
|
73
|
-
#
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
89
|
+
# Can be called multiple times with different datasets for a multi-valued
|
90
|
+
# graph.
|
91
|
+
#
|
92
|
+
# If the color argument is nil, the next color from the default theme will
|
93
|
+
# be used.
|
94
|
+
#
|
95
|
+
# @param name [String, Symbol] The name of the dataset.
|
96
|
+
# @param data_points [Array] The array of dataset.
|
97
|
+
# @param color [String] The color for drawing graph of dataset.
|
98
|
+
#
|
99
|
+
# @note
|
100
|
+
# If you want to use a preset theme, you must set it before calling {#data}.
|
101
|
+
#
|
102
|
+
# @example
|
103
|
+
# data("Bart S.", [95, 45, 78, 89, 88, 76], '#ffcc00')
|
104
|
+
def data(name, data_points = [], color = nil)
|
105
|
+
store.add(name, nil, data_points, color)
|
82
106
|
end
|
83
107
|
|
84
108
|
# This method allows one to plot a dataset with both X and Y data.
|
@@ -131,7 +155,7 @@ class Gruff::Line < Gruff::Base
|
|
131
155
|
raise ArgumentError, 'x_data_points.length != y_data_points.length!' if x_data_points.length != y_data_points.length
|
132
156
|
|
133
157
|
# call the existing data routine for the x/y data.
|
134
|
-
store.add(name, y_data_points, color
|
158
|
+
store.add(name, x_data_points, y_data_points, color)
|
135
159
|
end
|
136
160
|
|
137
161
|
private
|
@@ -165,7 +189,7 @@ private
|
|
165
189
|
end
|
166
190
|
|
167
191
|
def draw_horizontal_reference_line(reference_line)
|
168
|
-
level = @graph_top + (@graph_height - reference_line[:norm_value] * @graph_height)
|
192
|
+
level = @graph_top + (@graph_height - (reference_line[:norm_value] * @graph_height))
|
169
193
|
draw_reference_line(reference_line, @graph_left, @graph_left + @graph_width, level, level)
|
170
194
|
end
|
171
195
|
|
@@ -176,7 +200,8 @@ private
|
|
176
200
|
|
177
201
|
def draw_graph
|
178
202
|
# Check to see if more than one datapoint was given. NaN can result otherwise.
|
179
|
-
@x_increment = column_count > 1 ?
|
203
|
+
@x_increment = column_count > 1 ? @graph_width / (column_count - 1) : @graph_width
|
204
|
+
@x_increment = @x_increment.to_f
|
180
205
|
|
181
206
|
@reference_lines.each_value do |curr_reference_line|
|
182
207
|
draw_horizontal_reference_line(curr_reference_line) if curr_reference_line.key?(:norm_value)
|
@@ -189,22 +214,22 @@ private
|
|
189
214
|
one_point = contains_one_point_only?(data_row)
|
190
215
|
|
191
216
|
data_row.coordinates.each_with_index do |(x_data, y_data), index|
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
@labels.each do |label_pos, _|
|
199
|
-
draw_label(@graph_left + ((label_pos - @minimum_x_value) * @graph_width) / (@maximum_x_value - @minimum_x_value), label_pos)
|
217
|
+
new_x = begin
|
218
|
+
if x_data.nil?
|
219
|
+
# use the old method: equally spaced points along the x-axis
|
220
|
+
@graph_left + (@x_increment * index)
|
221
|
+
else
|
222
|
+
@graph_left + (x_data * @graph_width)
|
200
223
|
end
|
201
224
|
end
|
225
|
+
draw_label_for_x_data(x_data, new_x, index)
|
226
|
+
|
202
227
|
unless y_data # we can't draw a line for a null data point, we can still label the axis though
|
203
228
|
prev_x = prev_y = nil
|
204
229
|
next
|
205
230
|
end
|
206
231
|
|
207
|
-
new_y = @graph_top + (@graph_height - y_data * @graph_height)
|
232
|
+
new_y = @graph_top + (@graph_height - (y_data * @graph_height))
|
208
233
|
|
209
234
|
# Reset each time to avoid thin-line errors
|
210
235
|
stroke_width = @line_width || clip_value_if_greater_than(@columns / (store.norm_data.first.y_points.size * 4), 5.0)
|
@@ -227,12 +252,12 @@ private
|
|
227
252
|
|
228
253
|
def setup_data
|
229
254
|
# Update the global min/max values for the x data
|
230
|
-
@maximum_x_value
|
231
|
-
@minimum_x_value
|
255
|
+
@maximum_x_value = (@maximum_x_value || store.max_x).to_f
|
256
|
+
@minimum_x_value = (@minimum_x_value || store.min_x).to_f
|
232
257
|
|
233
258
|
# Deal with horizontal reference line values that exceed the existing minimum & maximum values.
|
234
|
-
possible_maximums = [maximum_value
|
235
|
-
possible_minimums = [minimum_value
|
259
|
+
possible_maximums = [maximum_value]
|
260
|
+
possible_minimums = [minimum_value]
|
236
261
|
|
237
262
|
@reference_lines.each_value do |curr_reference_line|
|
238
263
|
if curr_reference_line.key?(:value)
|
@@ -255,14 +280,14 @@ private
|
|
255
280
|
def normalize
|
256
281
|
return unless data_given?
|
257
282
|
|
258
|
-
spread_x = @maximum_x_value
|
283
|
+
spread_x = @maximum_x_value - @minimum_x_value
|
259
284
|
store.normalize(minimum_x: @minimum_x_value, spread_x: spread_x, minimum_y: minimum_value, spread_y: @spread)
|
260
285
|
|
261
286
|
@reference_lines.each_value do |curr_reference_line|
|
262
287
|
# We only care about horizontal markers ... for normalization.
|
263
288
|
# Vertical markers won't have a :value, they will have an :index
|
264
289
|
|
265
|
-
curr_reference_line[:norm_value] = ((curr_reference_line[:value].to_f - minimum_value) / @spread
|
290
|
+
curr_reference_line[:norm_value] = ((curr_reference_line[:value].to_f - minimum_value) / @spread) if curr_reference_line.key?(:value)
|
266
291
|
end
|
267
292
|
end
|
268
293
|
|
@@ -270,22 +295,21 @@ private
|
|
270
295
|
# do all of the stuff for the horizontal lines on the y-axis
|
271
296
|
super
|
272
297
|
return if @hide_line_markers
|
298
|
+
return unless @show_vertical_markers
|
273
299
|
|
274
300
|
(0..@marker_x_count).each do |index|
|
275
|
-
|
276
|
-
x = @graph_left + @graph_width - index * @graph_width / @marker_x_count
|
277
|
-
|
278
|
-
Gruff::Renderer::Line.new(renderer, color: @marker_color).render(x, @graph_bottom, x, @graph_top)
|
279
|
-
# If the user specified a marker shadow color, draw a shadow just below it
|
280
|
-
if @marker_shadow_color
|
281
|
-
Gruff::Renderer::Line.new(renderer, color: @marker_shadow_color).render(x + 1, @graph_bottom, x + 1, @graph_top)
|
282
|
-
end
|
283
|
-
end
|
301
|
+
draw_marker_vertical_line(@graph_left + @graph_width - (index * @graph_width / @marker_x_count))
|
284
302
|
end
|
285
303
|
end
|
286
304
|
|
287
|
-
def
|
288
|
-
|
305
|
+
def draw_label_for_x_data(x_data, new_x, index)
|
306
|
+
if x_data.nil?
|
307
|
+
draw_label(new_x, index)
|
308
|
+
else
|
309
|
+
@labels.each do |label_pos, _|
|
310
|
+
draw_label(@graph_left + (((label_pos - @minimum_x_value) * @graph_width) / (@maximum_x_value - @minimum_x_value)), label_pos)
|
311
|
+
end
|
312
|
+
end
|
289
313
|
end
|
290
314
|
|
291
315
|
def contains_one_point_only?(data_row)
|
data/lib/gruff/mini/legend.rb
CHANGED
@@ -21,7 +21,7 @@ module Gruff
|
|
21
21
|
|
22
22
|
@legend_labels = store.data.map(&:label)
|
23
23
|
|
24
|
-
legend_height =
|
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 =
|
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
|
-
|
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,17 +90,21 @@ 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 =
|
98
|
-
max_width = @columns -
|
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 ? '...' : '')
|
103
103
|
end
|
104
|
+
|
105
|
+
def scale(value)
|
106
|
+
value * @scale
|
107
|
+
end
|
104
108
|
end
|
105
109
|
end
|
106
110
|
end
|
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
|
data/lib/gruff/patch/string.rb
CHANGED
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 {#
|
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
|
+
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
|
19
|
-
attr_writer :
|
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.
|
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
|
-
|
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
|
-
|
38
|
-
|
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=} 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
|
-
@
|
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 ||= @
|
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,15 +3,17 @@
|
|
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
15
|
@renderer.draw.fill(@color)
|
16
|
+
@renderer.draw.fill_opacity(@opacity)
|
15
17
|
@renderer.draw.stroke(@color)
|
16
18
|
@renderer.draw.stroke_width(@width)
|
17
19
|
@renderer.draw.circle(origin_x, origin_y, perim_x, perim_y)
|
@@ -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(
|
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
|
data/lib/gruff/renderer/dot.rb
CHANGED
@@ -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,
|
14
|
-
|
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
|
-
|
19
|
-
|
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,
|
26
|
+
circle(new_x, new_y, radius)
|
22
27
|
end
|
23
|
-
|
28
|
+
@renderer.draw.pop
|
24
29
|
end
|
25
30
|
|
26
|
-
def circle(new_x, new_y,
|
27
|
-
@renderer.draw.circle(new_x, new_y, new_x -
|
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,
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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
|
data/lib/gruff/renderer/line.rb
CHANGED
@@ -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
|
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.
|
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
|