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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +28 -12
- data/.gitignore +1 -0
- data/.rubocop.yml +20 -24
- data/CHANGELOG.md +52 -0
- data/README.md +10 -3
- data/gruff.gemspec +9 -10
- data/lib/gruff/accumulator_bar.rb +1 -1
- data/lib/gruff/area.rb +6 -4
- data/lib/gruff/bar.rb +53 -31
- data/lib/gruff/base.rb +292 -184
- data/lib/gruff/bezier.rb +4 -2
- data/lib/gruff/box_plot.rb +180 -0
- data/lib/gruff/bullet.rb +6 -6
- 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 -40
- data/lib/gruff/helper/stacked_mixin.rb +19 -1
- data/lib/gruff/histogram.rb +9 -5
- data/lib/gruff/line.rb +49 -48
- data/lib/gruff/mini/legend.rb +11 -11
- data/lib/gruff/net.rb +23 -18
- data/lib/gruff/patch/rmagick.rb +0 -1
- data/lib/gruff/patch/string.rb +1 -0
- data/lib/gruff/pie.rb +26 -12
- 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 +4 -8
- data/lib/gruff/renderer/text.rb +7 -1
- data/lib/gruff/scatter.rb +64 -56
- data/lib/gruff/side_bar.rb +64 -30
- data/lib/gruff/side_stacked_bar.rb +43 -54
- data/lib/gruff/spider.rb +52 -18
- data/lib/gruff/stacked_area.rb +18 -8
- data/lib/gruff/stacked_bar.rb +59 -29
- data/lib/gruff/store/xy_data.rb +2 -0
- data/lib/gruff/version.rb +1 -1
- data/lib/gruff.rb +67 -58
- metadata +17 -16
- data/.rubocop_todo.yml +0 -116
- data/lib/gruff/scene.rb +0 -200
- 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 =
|
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
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
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
|
229
|
-
@minimum_x_value
|
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
|
233
|
-
possible_minimums = [minimum_value
|
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
|
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
|
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
|
-
|
278
|
-
|
276
|
+
draw_marker_vertical_line(@graph_left + @graph_width - (index * @graph_width / @marker_x_count))
|
277
|
+
end
|
278
|
+
end
|
279
279
|
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
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)
|
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,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 =
|
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 ? '...' : '')
|
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/rmagick.rb
CHANGED
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+ 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
|
-
@
|
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,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
|
@@ -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
|
-
|
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
|
-
|
96
|
+
Magick::Image.new(columns, rows) do |img|
|
97
|
+
img.background_color = 'transparent'
|
102
98
|
end
|
103
99
|
end
|
104
100
|
end
|
data/lib/gruff/renderer/text.rb
CHANGED
@@ -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
|