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
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
|
data/lib/gruff/scatter.rb
CHANGED
@@ -24,24 +24,35 @@ class Gruff::Scatter < Gruff::Base
|
|
24
24
|
attr_writer :circle_radius
|
25
25
|
attr_writer :stroke_width
|
26
26
|
|
27
|
-
# Allow disabling the significant rounding when labeling the X axis.
|
28
|
-
# This is useful when working with a small range of high values (for example, a date range of months, while seconds as units).
|
29
|
-
attr_writer :disable_significant_rounding_x_axis
|
30
|
-
|
31
27
|
# Allow for vertical marker lines.
|
32
28
|
attr_writer :show_vertical_markers
|
33
29
|
|
34
|
-
# Allow using vertical labels in the X axis (and setting the label margin).
|
35
|
-
attr_writer :x_label_margin
|
36
|
-
attr_writer :use_vertical_x_labels
|
37
|
-
|
38
30
|
# Allow enabling vertical lines. When you have a lot of data, they can work great.
|
39
|
-
# @deprecated Please use
|
31
|
+
# @deprecated Please use {#show_vertical_markers=} instead.
|
40
32
|
def enable_vertical_line_markers=(value)
|
41
33
|
warn '#enable_vertical_line_markers= is deprecated. Please use `show_vertical_markers` attribute instead'
|
42
34
|
@show_vertical_markers = value
|
43
35
|
end
|
44
36
|
|
37
|
+
# Allow using vertical labels in the X axis.
|
38
|
+
# @deprecated Please use {Gruff::Base#label_rotation=} instead.
|
39
|
+
def use_vertical_x_labels=(_value)
|
40
|
+
warn '#use_vertical_x_labels= is deprecated. It is no longer effective. Please use `#label_rotation=` instead'
|
41
|
+
end
|
42
|
+
|
43
|
+
# Allow using vertical labels in the X axis (and setting the label margin).
|
44
|
+
# @deprecated
|
45
|
+
def x_label_margin=(_value)
|
46
|
+
warn '#x_label_margin= is deprecated. It is no longer effective.'
|
47
|
+
end
|
48
|
+
|
49
|
+
# Allow disabling the significant rounding when labeling the X axis.
|
50
|
+
# This is useful when working with a small range of high values (for example, a date range of months, while seconds as units).
|
51
|
+
# @deprecated
|
52
|
+
def disable_significant_rounding_x_axis=(_value)
|
53
|
+
warn '#disable_significant_rounding_x_axis= is deprecated. It is no longer effective.'
|
54
|
+
end
|
55
|
+
|
45
56
|
# The first parameter is the name of the dataset. The next two are the
|
46
57
|
# x and y axis data points contain in their own array in that respective
|
47
58
|
# order. The final parameter is the color.
|
@@ -55,11 +66,10 @@ class Gruff::Scatter < Gruff::Base
|
|
55
66
|
# @note If you want to use a preset theme, you must set it before calling {#data}.
|
56
67
|
#
|
57
68
|
# @param name [String, Symbol] containing the name of the dataset.
|
58
|
-
# @param x_data_points [Array] An Array of
|
59
|
-
# @param y_data_points [Array] An Array of
|
69
|
+
# @param x_data_points [Array] An Array of x-axis data points.
|
70
|
+
# @param y_data_points [Array] An Array of y-axis data points.
|
60
71
|
# @param color [String] The hex string for the color of the dataset. Defaults to nil.
|
61
72
|
#
|
62
|
-
#
|
63
73
|
# @raise [ArgumentError] Data points contain nil values.
|
64
74
|
# This error will get raised if either the x or y axis data points array
|
65
75
|
# contains a +nil+ value. The graph will not make an assumption
|
@@ -88,7 +98,7 @@ class Gruff::Scatter < Gruff::Base
|
|
88
98
|
raise ArgumentError, 'x_data_points.length != y_data_points.length!' if x_data_points.length != y_data_points.length
|
89
99
|
|
90
100
|
# Call the existing data routine for the x/y axis data
|
91
|
-
store.add(name, y_data_points, color
|
101
|
+
store.add(name, x_data_points, y_data_points, color)
|
92
102
|
end
|
93
103
|
|
94
104
|
alias dataxy data
|
@@ -102,56 +112,55 @@ private
|
|
102
112
|
def initialize_attributes
|
103
113
|
super
|
104
114
|
|
105
|
-
@baseline_x_color = @baseline_y_color = 'red'
|
106
|
-
@baseline_x_value = @baseline_y_value = nil
|
107
115
|
@circle_radius = nil
|
108
|
-
@disable_significant_rounding_x_axis = false
|
109
116
|
@show_vertical_markers = false
|
110
117
|
@marker_x_count = nil
|
111
118
|
@maximum_x_value = @minimum_x_value = nil
|
112
119
|
@stroke_width = nil
|
113
|
-
@use_vertical_x_labels = false
|
114
|
-
@x_label_margin = nil
|
115
120
|
end
|
116
121
|
|
117
|
-
def
|
118
|
-
|
119
|
-
|
122
|
+
def setup_drawing
|
123
|
+
@center_labels_over_point = false
|
124
|
+
super
|
125
|
+
end
|
120
126
|
|
127
|
+
def setup_data
|
128
|
+
# TODO: Need to get x-axis labels working. Current behavior will be to not allow.
|
129
|
+
@labels = {}
|
130
|
+
|
131
|
+
# Update the global min/max values for the x data
|
132
|
+
@maximum_x_value = (@maximum_x_value || store.max_x).to_f
|
133
|
+
@minimum_x_value = (@minimum_x_value || store.min_x).to_f
|
134
|
+
|
135
|
+
if @x_axis_increment
|
136
|
+
# TODO: Make this work for negative values
|
137
|
+
@maximum_x_value = [@maximum_x_value.ceil, @x_axis_increment.to_f].max
|
138
|
+
@minimum_x_value = @minimum_x_value.floor
|
139
|
+
end
|
140
|
+
|
141
|
+
super
|
142
|
+
end
|
143
|
+
|
144
|
+
def draw_graph
|
121
145
|
store.norm_data.each do |data_row|
|
122
146
|
data_row.coordinates.each do |x_value, y_value|
|
123
147
|
next if y_value.nil? || x_value.nil?
|
124
148
|
|
125
|
-
new_x =
|
126
|
-
new_y = @
|
149
|
+
new_x = @graph_left + (x_value * @graph_width)
|
150
|
+
new_y = @graph_bottom - (y_value * @graph_height)
|
127
151
|
|
128
152
|
# 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)
|
153
|
+
stroke_width = @stroke_width || clip_value_if_greater_than(@columns / (store.norm_data.first[1].size * 4.0), 5.0)
|
130
154
|
circle_radius = @circle_radius || clip_value_if_greater_than(@columns / (store.norm_data.first[1].size * 2.5), 5.0)
|
131
155
|
Gruff::Renderer::Circle.new(renderer, color: data_row.color, width: stroke_width).render(new_x, new_y, new_x - circle_radius, new_y)
|
132
156
|
end
|
133
157
|
end
|
134
158
|
end
|
135
159
|
|
136
|
-
def setup_data
|
137
|
-
# Update the global min/max values for the x data
|
138
|
-
@maximum_x_value ||= store.max_x
|
139
|
-
@minimum_x_value ||= store.min_x
|
140
|
-
|
141
|
-
super
|
142
|
-
end
|
143
|
-
|
144
|
-
def setup_drawing
|
145
|
-
# TODO: Need to get x-axis labels working. Current behavior will be to not allow.
|
146
|
-
@labels = {}
|
147
|
-
|
148
|
-
super
|
149
|
-
end
|
150
|
-
|
151
160
|
def calculate_spread
|
152
161
|
super
|
153
162
|
@x_spread = @maximum_x_value.to_f - @minimum_x_value.to_f
|
154
|
-
@x_spread = @x_spread > 0 ? @x_spread : 1
|
163
|
+
@x_spread = @x_spread > 0 ? @x_spread : 1.0
|
155
164
|
end
|
156
165
|
|
157
166
|
def normalize
|
@@ -165,57 +174,51 @@ private
|
|
165
174
|
super
|
166
175
|
return if @hide_line_markers
|
167
176
|
|
168
|
-
|
169
|
-
# TODO: Do the same for larger numbers...100, 75, 50, 25
|
170
|
-
if @marker_x_count.nil?
|
171
|
-
(3..7).each do |lines|
|
172
|
-
if @x_spread % lines == 0.0
|
173
|
-
@marker_x_count = lines
|
174
|
-
break
|
175
|
-
end
|
176
|
-
end
|
177
|
-
@marker_x_count ||= 4
|
178
|
-
end
|
179
|
-
@x_increment = @x_spread > 0 ? (@x_spread / @marker_x_count) : 1
|
180
|
-
unless @disable_significant_rounding_x_axis
|
181
|
-
@x_increment = significant(@x_increment)
|
182
|
-
end
|
183
|
-
else
|
184
|
-
# TODO: Make this work for negative values
|
185
|
-
@maximum_x_value = [@maximum_x_value.ceil, @x_axis_increment].max
|
186
|
-
@minimum_x_value = @minimum_x_value.floor
|
187
|
-
calculate_spread
|
188
|
-
normalize
|
189
|
-
|
190
|
-
@marker_x_count = (@x_spread / @x_axis_increment).to_i
|
191
|
-
@x_increment = @x_axis_increment
|
192
|
-
end
|
193
|
-
increment_x_scaled = @graph_width.to_f / (@x_spread / @x_increment)
|
177
|
+
increment_x_scaled = (@graph_width / (@x_spread / x_increment)).to_f
|
194
178
|
|
195
179
|
# Draw vertical line markers and annotate with numbers
|
196
|
-
(0
|
180
|
+
(0..marker_x_count).each do |index|
|
197
181
|
# TODO: Fix the vertical lines, and enable them by default. Not pretty when they don't match up with top y-axis line
|
198
182
|
if @show_vertical_markers
|
199
|
-
|
200
|
-
|
201
|
-
line_renderer = Gruff::Renderer::Line.new(renderer, color: @marker_color, shadow_color: @marker_shadow_color)
|
202
|
-
line_renderer.render(x, @graph_top, x, @graph_bottom)
|
183
|
+
draw_marker_vertical_line(@graph_left + (index * increment_x_scaled))
|
203
184
|
end
|
204
185
|
|
205
186
|
unless @hide_line_numbers
|
206
|
-
marker_label = BigDecimal(index.to_s) * BigDecimal(
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
text_renderer.add_to_render_queue(1.0, 1.0, x_offset, y_offset)
|
187
|
+
marker_label = (BigDecimal(index.to_s) * BigDecimal(x_increment.to_s)) + BigDecimal(@minimum_x_value.to_s)
|
188
|
+
label = x_axis_label(marker_label, x_increment)
|
189
|
+
x = @graph_left + (increment_x_scaled * index)
|
190
|
+
y = @graph_bottom
|
191
|
+
x_offset, y_offset = calculate_label_offset(@marker_font, label, LABEL_MARGIN, @label_rotation)
|
192
|
+
|
193
|
+
draw_label_at(1.0, 1.0, x + x_offset, y + y_offset, label, rotation: @label_rotation)
|
214
194
|
end
|
215
195
|
end
|
216
196
|
end
|
217
197
|
|
218
|
-
def
|
219
|
-
|
198
|
+
def marker_x_count
|
199
|
+
# TODO: Do the same for larger numbers...100, 75, 50, 25
|
200
|
+
@marker_x_count ||= begin
|
201
|
+
if @x_axis_increment.nil?
|
202
|
+
count = nil
|
203
|
+
(3..7).each do |lines|
|
204
|
+
if @x_spread % lines == 0.0
|
205
|
+
count = lines and break
|
206
|
+
end
|
207
|
+
end
|
208
|
+
count || 4
|
209
|
+
else
|
210
|
+
(@x_spread / @x_axis_increment).to_i
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def x_increment
|
216
|
+
@x_increment ||= begin
|
217
|
+
if @x_axis_increment.nil?
|
218
|
+
@x_spread > 0 ? significant(@x_spread / marker_x_count) : 1.0
|
219
|
+
else
|
220
|
+
@x_axis_increment.to_f
|
221
|
+
end
|
222
|
+
end
|
220
223
|
end
|
221
224
|
end
|
data/lib/gruff/side_bar.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'helper/bar_mixin'
|
4
|
+
|
3
5
|
# Graph with individual horizontal bars instead of vertical bars.
|
4
6
|
#
|
5
7
|
# Here's how to set up a Gruff::SideBar.
|
@@ -19,6 +21,8 @@
|
|
19
21
|
# g.write('sidebar.png')
|
20
22
|
#
|
21
23
|
class Gruff::SideBar < Gruff::Base
|
24
|
+
include BarMixin
|
25
|
+
|
22
26
|
# Spacing factor applied between bars.
|
23
27
|
attr_writer :bar_spacing
|
24
28
|
|
@@ -36,6 +40,15 @@ class Gruff::SideBar < Gruff::Base
|
|
36
40
|
# Prevent drawing of column labels left of a side bar graph. Default is +false+.
|
37
41
|
attr_writer :hide_labels
|
38
42
|
|
43
|
+
# Value to avoid completely overwriting the coordinate axis
|
44
|
+
AXIS_MARGIN = 0.5
|
45
|
+
private_constant :AXIS_MARGIN
|
46
|
+
|
47
|
+
def initialize(*)
|
48
|
+
super
|
49
|
+
@has_left_labels = true
|
50
|
+
end
|
51
|
+
|
39
52
|
# With Side Bars use the data label for the marker value to the left of the bar.
|
40
53
|
# @deprecated
|
41
54
|
def use_data_label=(_value)
|
@@ -51,7 +64,6 @@ private
|
|
51
64
|
@label_formatting = nil
|
52
65
|
@show_labels_for_bar_values = false
|
53
66
|
@hide_labels = false
|
54
|
-
@has_left_labels = true
|
55
67
|
end
|
56
68
|
|
57
69
|
def hide_labels?
|
@@ -59,20 +71,37 @@ private
|
|
59
71
|
end
|
60
72
|
|
61
73
|
def hide_left_label_area?
|
62
|
-
hide_labels?
|
74
|
+
hide_labels? && @y_axis_label.nil?
|
63
75
|
end
|
64
76
|
|
65
77
|
def hide_bottom_label_area?
|
66
|
-
@hide_line_markers
|
78
|
+
@hide_line_markers && @x_axis_label.nil? && @legend_at_bottom == false
|
67
79
|
end
|
68
80
|
|
69
|
-
|
70
|
-
|
81
|
+
def setup_graph_measurements
|
82
|
+
super
|
83
|
+
return if @hide_line_markers
|
84
|
+
|
85
|
+
if @show_labels_for_bar_values
|
86
|
+
if maximum_value >= 0
|
87
|
+
_, metrics = Gruff::BarValueLabel.metrics(maximum_value, @label_formatting, proc_text_metrics)
|
88
|
+
@graph_right -= metrics.width
|
89
|
+
end
|
90
|
+
|
91
|
+
if minimum_value < 0
|
92
|
+
_, metrics = Gruff::BarValueLabel.metrics(minimum_value, @label_formatting, proc_text_metrics)
|
93
|
+
width = metrics.width + LABEL_MARGIN
|
94
|
+
@graph_left += width - @graph_left if width > @graph_left
|
95
|
+
end
|
96
|
+
|
97
|
+
@graph_width = @graph_right - @graph_left
|
98
|
+
end
|
99
|
+
end
|
71
100
|
|
72
101
|
def draw_graph
|
73
102
|
# Setup spacing.
|
74
103
|
#
|
75
|
-
bars_width = (@graph_height - calculate_spacing) / column_count
|
104
|
+
bars_width = (@graph_height - calculate_spacing) / column_count
|
76
105
|
bar_width = bars_width / store.length
|
77
106
|
padding = (bar_width * (1 - @bar_spacing)) / 2
|
78
107
|
|
@@ -82,30 +111,32 @@ private
|
|
82
111
|
minimum_value: minimum_value, maximum_value: maximum_value, spread: @spread
|
83
112
|
)
|
84
113
|
|
85
|
-
|
86
|
-
data_row.points.each_with_index do |data_point, point_index|
|
87
|
-
group_spacing = @group_spacing * @scale * point_index
|
88
|
-
|
89
|
-
left_y = @graph_top + (bars_width * point_index) + (bar_width * row_index) + padding + group_spacing
|
90
|
-
right_y = left_y + bar_width * @bar_spacing
|
114
|
+
group_left_y = @graph_top
|
91
115
|
|
92
|
-
|
116
|
+
normalized_group_bars.each_with_index do |group_bars, group_index|
|
117
|
+
right_y = 0
|
118
|
+
group_bars.each_with_index do |bar, index|
|
119
|
+
left_y = group_left_y + (bar_width * index) + padding
|
120
|
+
right_y = left_y + (bar_width * @bar_spacing)
|
93
121
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
122
|
+
bottom_x, top_x = conversion.get_top_bottom_scaled(bar.point).sort
|
123
|
+
if bar.point != 0
|
124
|
+
rect_renderer = Gruff::Renderer::Rectangle.new(renderer, color: bar.color)
|
125
|
+
rect_renderer.render(bottom_x + AXIS_MARGIN, left_y, top_x, right_y)
|
126
|
+
end
|
99
127
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
bar_value_label.prepare_rendering(@label_formatting, bar_width) do |x, y, text|
|
105
|
-
draw_value_label(x, y, text)
|
128
|
+
if @show_labels_for_bar_values && bar.value
|
129
|
+
bar_value_label = Gruff::BarValueLabel::SideBar.new([bottom_x, left_y, top_x, right_y], bar.value)
|
130
|
+
bar_value_label.prepare_rendering(@label_formatting, proc_text_metrics) do |x, y, text, text_width, _text_height|
|
131
|
+
draw_value_label(text_width, bar_width * @bar_spacing, x, y, text)
|
106
132
|
end
|
107
133
|
end
|
108
134
|
end
|
135
|
+
|
136
|
+
label_center = group_left_y + (bars_width / 2.0)
|
137
|
+
draw_label(label_center, group_index)
|
138
|
+
|
139
|
+
group_left_y = right_y + padding + @group_spacing
|
109
140
|
end
|
110
141
|
end
|
111
142
|
|
@@ -118,17 +149,15 @@ private
|
|
118
149
|
number_of_lines = 1 if number_of_lines == 0
|
119
150
|
|
120
151
|
# TODO: Round maximum marker value to a round number like 100, 0.1, 0.5, etc.
|
121
|
-
increment = significant(@spread
|
152
|
+
increment = significant(@spread / number_of_lines)
|
122
153
|
(0..number_of_lines).each do |index|
|
123
154
|
line_diff = (@graph_right - @graph_left) / number_of_lines
|
124
155
|
x = @graph_right - (line_diff * index) - 1
|
125
|
-
|
126
|
-
line_renderer = Gruff::Renderer::Line.new(renderer, color: @marker_color, shadow_color: @marker_shadow_color)
|
127
|
-
line_renderer.render(x, @graph_bottom, x, @graph_top)
|
156
|
+
draw_marker_vertical_line(x)
|
128
157
|
|
129
158
|
unless @hide_line_numbers
|
130
159
|
diff = index - number_of_lines
|
131
|
-
marker_label = BigDecimal(diff.abs.to_s) * BigDecimal(increment.to_s) + BigDecimal(minimum_value.to_s)
|
160
|
+
marker_label = (BigDecimal(diff.abs.to_s) * BigDecimal(increment.to_s)) + BigDecimal(minimum_value.to_s)
|
132
161
|
label = x_axis_label(marker_label, @increment)
|
133
162
|
text_renderer = Gruff::Renderer::Text.new(renderer, label, font: @marker_font)
|
134
163
|
text_renderer.add_to_render_queue(0, 0, x, @graph_bottom + LABEL_MARGIN, Magick::CenterGravity)
|
@@ -141,11 +170,15 @@ private
|
|
141
170
|
|
142
171
|
def draw_label(y_offset, index)
|
143
172
|
draw_unique_label(index) do
|
144
|
-
draw_label_at(@graph_left - LABEL_MARGIN, 1.0, 0.0, y_offset, @labels[index], Magick::EastGravity)
|
173
|
+
draw_label_at(@graph_left - LABEL_MARGIN, 1.0, 0.0, y_offset, @labels[index], gravity: Magick::EastGravity)
|
145
174
|
end
|
146
175
|
end
|
147
176
|
|
148
177
|
def calculate_spacing
|
149
|
-
@
|
178
|
+
@group_spacing * (column_count - 1)
|
179
|
+
end
|
180
|
+
|
181
|
+
def proc_text_metrics
|
182
|
+
->(text) { text_metrics(@marker_font, text) }
|
150
183
|
end
|
151
184
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'helper/stacked_mixin'
|
4
|
+
|
3
5
|
#
|
4
6
|
# New gruff graph type added to enable sideways stacking bar charts
|
5
7
|
# (basically looks like a x/y flip of a standard stacking bar chart)
|
@@ -39,6 +41,11 @@ class Gruff::SideStackedBar < Gruff::SideBar
|
|
39
41
|
# Prevent drawing of column labels left of a side stacked bar graph. Default is +false+.
|
40
42
|
attr_writer :hide_labels
|
41
43
|
|
44
|
+
def initialize(*)
|
45
|
+
super
|
46
|
+
@has_left_labels = true
|
47
|
+
end
|
48
|
+
|
42
49
|
private
|
43
50
|
|
44
51
|
def initialize_attributes
|
@@ -48,7 +55,7 @@ private
|
|
48
55
|
@label_formatting = nil
|
49
56
|
@show_labels_for_bar_values = false
|
50
57
|
@hide_labels = false
|
51
|
-
@
|
58
|
+
@minimum_value = 0.0
|
52
59
|
end
|
53
60
|
|
54
61
|
def setup_data
|
@@ -56,67 +63,48 @@ private
|
|
56
63
|
super
|
57
64
|
end
|
58
65
|
|
59
|
-
def hide_labels?
|
60
|
-
@hide_labels
|
61
|
-
end
|
62
|
-
|
63
|
-
def hide_left_label_area?
|
64
|
-
hide_labels?
|
65
|
-
end
|
66
|
-
|
67
|
-
def hide_bottom_label_area?
|
68
|
-
@hide_line_markers
|
69
|
-
end
|
70
|
-
|
71
66
|
def draw_graph
|
72
67
|
# Setup spacing.
|
73
68
|
#
|
74
69
|
# Columns sit stacked.
|
75
|
-
bar_width = @graph_height / column_count
|
76
|
-
height = Array.new(column_count, 0)
|
77
|
-
length = Array.new(column_count, @graph_left)
|
70
|
+
bar_width = @graph_height / column_count
|
78
71
|
padding = (bar_width * (1 - @bar_spacing)) / 2
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
if data_point != 0
|
106
|
-
rect_renderer = Gruff::Renderer::Rectangle.new(renderer, color: data_row.color)
|
107
|
-
rect_renderer.render(left_x, left_y, right_x, right_y)
|
108
|
-
# Calculate center based on bar_width and current row
|
109
|
-
end
|
110
|
-
# we still need to draw the labels
|
111
|
-
# Calculate center based on bar_width and current row
|
112
|
-
label_center = left_y + bar_width / 2
|
113
|
-
draw_label(label_center, point_index)
|
72
|
+
|
73
|
+
# Setup the BarConversion Object
|
74
|
+
conversion = Gruff::BarConversion.new(
|
75
|
+
top: @graph_right, bottom: @graph_left,
|
76
|
+
minimum_value: minimum_value, maximum_value: maximum_value, spread: @spread
|
77
|
+
)
|
78
|
+
|
79
|
+
proc_text_metrics = ->(text) { text_metrics(@marker_font, text) }
|
80
|
+
|
81
|
+
normalized_stacked_bars.each_with_index do |stacked_bars, stacked_index|
|
82
|
+
total = 0
|
83
|
+
left_y = @graph_top + (bar_width * stacked_index) + padding
|
84
|
+
right_y = left_y + (bar_width * @bar_spacing)
|
85
|
+
|
86
|
+
top_x = 0
|
87
|
+
stacked_bars.each do |bar|
|
88
|
+
next if bar.point == 0
|
89
|
+
|
90
|
+
bottom_x, = conversion.get_top_bottom_scaled(total)
|
91
|
+
bottom_x += @segment_spacing
|
92
|
+
top_x, = conversion.get_top_bottom_scaled(total + bar.point)
|
93
|
+
|
94
|
+
rect_renderer = Gruff::Renderer::Rectangle.new(renderer, color: bar.color)
|
95
|
+
rect_renderer.render(bottom_x, left_y, top_x, right_y)
|
96
|
+
|
97
|
+
total += bar.point
|
114
98
|
end
|
115
|
-
end
|
116
99
|
|
117
|
-
|
118
|
-
|
119
|
-
|
100
|
+
label_center = left_y + (bar_width / 2.0)
|
101
|
+
draw_label(label_center, stacked_index)
|
102
|
+
|
103
|
+
if @show_labels_for_bar_values
|
104
|
+
bar_value_label = Gruff::BarValueLabel::SideBar.new([@graph_left, left_y, top_x, right_y], stacked_bars.sum(&:value))
|
105
|
+
bar_value_label.prepare_rendering(@label_formatting, proc_text_metrics) do |x, y, text, text_width, _text_height|
|
106
|
+
draw_value_label(text_width, bar_width * @bar_spacing, x, y, text)
|
107
|
+
end
|
120
108
|
end
|
121
109
|
end
|
122
110
|
end
|