gruff 0.5.1-java → 0.10.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 +5 -5
- data/.editorconfig +14 -0
- data/.github/ISSUE_TEMPLATE.md +18 -0
- data/.gitignore +3 -0
- data/.rubocop.yml +109 -0
- data/.rubocop_todo.yml +130 -0
- data/.travis.yml +24 -12
- data/.yardopts +1 -0
- data/{History.txt → CHANGELOG.md} +61 -25
- data/Gemfile +3 -7
- data/README.md +57 -25
- data/Rakefile +6 -201
- data/assets/plastik/blue.png +0 -0
- data/assets/plastik/green.png +0 -0
- data/assets/plastik/red.png +0 -0
- data/docker/Dockerfile +14 -0
- data/docker/build.sh +4 -0
- data/docker/launch.sh +4 -0
- data/gruff.gemspec +19 -14
- data/init.rb +2 -0
- data/lib/gruff.rb +26 -2
- data/lib/gruff/accumulator_bar.rb +18 -8
- data/lib/gruff/area.rb +33 -19
- data/lib/gruff/bar.rb +76 -45
- data/lib/gruff/base.rb +337 -613
- data/lib/gruff/bezier.rb +34 -19
- data/lib/gruff/bullet.rb +51 -62
- data/lib/gruff/dot.rb +38 -62
- data/lib/gruff/helper/bar_conversion.rb +47 -0
- data/lib/gruff/helper/bar_value_label_mixin.rb +30 -0
- data/lib/gruff/helper/stacked_mixin.rb +23 -0
- data/lib/gruff/histogram.rb +59 -0
- data/lib/gruff/line.rb +130 -150
- data/lib/gruff/mini/bar.rb +17 -10
- data/lib/gruff/mini/legend.rb +24 -36
- data/lib/gruff/mini/pie.rb +18 -12
- data/lib/gruff/mini/side_bar.rb +26 -12
- data/lib/gruff/net.rb +60 -84
- data/lib/gruff/patch/rmagick.rb +33 -0
- data/lib/gruff/patch/string.rb +10 -0
- data/lib/gruff/photo_bar.rb +27 -30
- data/lib/gruff/pie.rb +190 -93
- data/lib/gruff/renderer/bezier.rb +21 -0
- data/lib/gruff/renderer/circle.rb +21 -0
- data/lib/gruff/renderer/dash_line.rb +22 -0
- data/lib/gruff/renderer/dot.rb +39 -0
- data/lib/gruff/renderer/ellipse.rb +21 -0
- data/lib/gruff/renderer/line.rb +34 -0
- data/lib/gruff/renderer/polygon.rb +23 -0
- data/lib/gruff/renderer/polyline.rb +21 -0
- data/lib/gruff/renderer/rectangle.rb +19 -0
- data/lib/gruff/renderer/renderer.rb +127 -0
- data/lib/gruff/renderer/text.rb +42 -0
- data/lib/gruff/scatter.rb +156 -180
- data/lib/gruff/scene.rb +31 -41
- data/lib/gruff/side_bar.rb +77 -63
- data/lib/gruff/side_stacked_bar.rb +77 -60
- data/lib/gruff/spider.rb +37 -50
- data/lib/gruff/stacked_area.rb +32 -30
- data/lib/gruff/stacked_bar.rb +87 -49
- data/lib/gruff/store/base_data.rb +34 -0
- data/lib/gruff/store/custom_data.rb +34 -0
- data/lib/gruff/store/store.rb +80 -0
- data/lib/gruff/store/xy_data.rb +55 -0
- data/lib/gruff/themes.rb +32 -33
- data/lib/gruff/version.rb +3 -1
- metadata +88 -94
- data/Manifest.txt +0 -81
- data/RELEASE.md +0 -30
- data/assets/bubble.png +0 -0
- data/assets/city_scene/background/0000.png +0 -0
- data/assets/city_scene/background/0600.png +0 -0
- data/assets/city_scene/background/2000.png +0 -0
- data/assets/city_scene/clouds/cloudy.png +0 -0
- data/assets/city_scene/clouds/partly_cloudy.png +0 -0
- data/assets/city_scene/clouds/stormy.png +0 -0
- data/assets/city_scene/grass/default.png +0 -0
- data/assets/city_scene/haze/true.png +0 -0
- data/assets/city_scene/number_sample/1.png +0 -0
- data/assets/city_scene/number_sample/2.png +0 -0
- data/assets/city_scene/number_sample/default.png +0 -0
- data/assets/city_scene/sky/0000.png +0 -0
- data/assets/city_scene/sky/0200.png +0 -0
- data/assets/city_scene/sky/0400.png +0 -0
- data/assets/city_scene/sky/0600.png +0 -0
- data/assets/city_scene/sky/0800.png +0 -0
- data/assets/city_scene/sky/1000.png +0 -0
- data/assets/city_scene/sky/1200.png +0 -0
- data/assets/city_scene/sky/1400.png +0 -0
- data/assets/city_scene/sky/1500.png +0 -0
- data/assets/city_scene/sky/1700.png +0 -0
- data/assets/city_scene/sky/2000.png +0 -0
- data/assets/pc306715.jpg +0 -0
- data/lib/gruff/bar_conversion.rb +0 -46
- data/lib/gruff/deprecated.rb +0 -39
- data/lib/gruff/stacked_mixin.rb +0 -23
- data/test/gruff_test_case.rb +0 -154
- data/test/image_compare.rb +0 -58
- data/test/test_accumulator_bar.rb +0 -51
- data/test/test_area.rb +0 -134
- data/test/test_bar.rb +0 -505
- data/test/test_base.rb +0 -8
- data/test/test_bezier.rb +0 -33
- data/test/test_bullet.rb +0 -26
- data/test/test_dot.rb +0 -263
- data/test/test_legend.rb +0 -68
- data/test/test_line.rb +0 -657
- data/test/test_mini_bar.rb +0 -33
- data/test/test_mini_pie.rb +0 -25
- data/test/test_mini_side_bar.rb +0 -36
- data/test/test_net.rb +0 -231
- data/test/test_photo.rb +0 -41
- data/test/test_pie.rb +0 -154
- data/test/test_scatter.rb +0 -233
- data/test/test_scene.rb +0 -100
- data/test/test_side_bar.rb +0 -56
- data/test/test_sidestacked_bar.rb +0 -105
- data/test/test_spider.rb +0 -226
- data/test/test_stacked_area.rb +0 -52
- data/test/test_stacked_bar.rb +0 -52
data/lib/gruff/spider.rb
CHANGED
|
@@ -1,20 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
|
|
2
|
-
require
|
|
3
|
+
require 'gruff/base'
|
|
3
4
|
|
|
4
5
|
# Experimental!!! See also the Net graph.
|
|
5
6
|
#
|
|
6
|
-
#
|
|
7
|
+
# Here's how to set up a Gruff::Spider.
|
|
8
|
+
#
|
|
9
|
+
# g = Gruff::Spider.new(30)
|
|
10
|
+
# g.title = "Spider Graph"
|
|
11
|
+
# g.data :Strength, [10]
|
|
12
|
+
# g.data :Dexterity, [16]
|
|
13
|
+
# g.data :Constitution, [12]
|
|
14
|
+
# g.data :Intelligence, [12]
|
|
15
|
+
# g.data :Wisdom, [10]
|
|
16
|
+
# g.data 'Charisma', [16]
|
|
17
|
+
# g.write("spider.png")
|
|
18
|
+
|
|
7
19
|
class Gruff::Spider < Gruff::Base
|
|
8
|
-
|
|
9
|
-
# Hide all text
|
|
20
|
+
# Hide all text.
|
|
10
21
|
attr_reader :hide_text
|
|
11
22
|
attr_accessor :hide_axes
|
|
12
23
|
attr_reader :transparent_background
|
|
13
24
|
attr_accessor :rotation
|
|
14
|
-
|
|
25
|
+
|
|
15
26
|
def transparent_background=(value)
|
|
16
|
-
@
|
|
17
|
-
@base_image = render_transparent_background if value
|
|
27
|
+
Gruff::Renderer.setup_transparent_background(@columns, @rows) if value
|
|
18
28
|
end
|
|
19
29
|
|
|
20
30
|
def hide_text=(value)
|
|
@@ -24,39 +34,33 @@ class Gruff::Spider < Gruff::Base
|
|
|
24
34
|
def initialize(max_value, target_width = 800)
|
|
25
35
|
super(target_width)
|
|
26
36
|
@max_value = max_value
|
|
27
|
-
@hide_legend = true
|
|
37
|
+
@hide_legend = true
|
|
28
38
|
@rotation = 0
|
|
29
39
|
end
|
|
30
|
-
|
|
40
|
+
|
|
31
41
|
def draw
|
|
32
42
|
@hide_line_markers = true
|
|
33
|
-
|
|
43
|
+
|
|
34
44
|
super
|
|
35
45
|
|
|
36
|
-
return unless
|
|
46
|
+
return unless data_given?
|
|
37
47
|
|
|
38
48
|
# Setup basic positioning
|
|
39
|
-
diameter = @graph_height
|
|
40
49
|
radius = @graph_height / 2.0
|
|
41
|
-
top_x = @graph_left + (@graph_width - diameter) / 2.0
|
|
42
50
|
center_x = @graph_left + (@graph_width / 2.0)
|
|
43
51
|
center_y = @graph_top + (@graph_height / 2.0) - 25 # Move graph up a bit
|
|
44
52
|
|
|
45
53
|
@unit_length = radius / @max_value
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
prev_degrees = 0.0
|
|
49
|
-
additive_angle = (2 * Math::PI)/ @data.size
|
|
50
|
-
|
|
51
|
-
current_angle = rotation * Math::PI / 180.0
|
|
54
|
+
|
|
55
|
+
additive_angle = (2 * Math::PI) / store.length
|
|
52
56
|
|
|
53
57
|
# Draw axes
|
|
54
|
-
draw_axes(center_x, center_y, radius, additive_angle) unless hide_axes
|
|
58
|
+
draw_axes(center_x, center_y, radius, additive_angle) unless hide_axes
|
|
55
59
|
|
|
56
60
|
# Draw polygon
|
|
57
61
|
draw_polygon(center_x, center_y, additive_angle)
|
|
58
62
|
|
|
59
|
-
|
|
63
|
+
Gruff::Renderer.finish
|
|
60
64
|
end
|
|
61
65
|
|
|
62
66
|
private
|
|
@@ -66,23 +70,15 @@ private
|
|
|
66
70
|
end
|
|
67
71
|
|
|
68
72
|
def draw_label(center_x, center_y, angle, radius, amount)
|
|
69
|
-
r_offset = 50
|
|
73
|
+
r_offset = 50 # The distance out from the center of the pie to get point
|
|
70
74
|
x_offset = center_x # The label points need to be tweaked slightly
|
|
71
75
|
y_offset = center_y + 0 # This one doesn't though
|
|
72
76
|
x = x_offset + ((radius + r_offset) * Math.cos(angle))
|
|
73
77
|
y = y_offset + ((radius + r_offset) * Math.sin(angle))
|
|
74
78
|
|
|
75
79
|
# Draw label
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
@d.pointsize = scale_fontsize(legend_font_size)
|
|
79
|
-
@d.stroke = 'transparent'
|
|
80
|
-
@d.font_weight = BoldWeight
|
|
81
|
-
@d.gravity = CenterGravity
|
|
82
|
-
@d.annotate_scaled( @base_image,
|
|
83
|
-
0, 0,
|
|
84
|
-
x, y,
|
|
85
|
-
amount, @scale)
|
|
80
|
+
text_renderer = Gruff::Renderer::Text.new(amount, font: @font, size: legend_font_size, color: @marker_color, weight: Magick::BoldWeight)
|
|
81
|
+
text_renderer.render(0, 0, x, y, Magick::CenterGravity)
|
|
86
82
|
end
|
|
87
83
|
|
|
88
84
|
def draw_axes(center_x, center_y, radius, additive_angle, line_color = nil)
|
|
@@ -90,18 +86,14 @@ private
|
|
|
90
86
|
|
|
91
87
|
current_angle = rotation * Math::PI / 180.0
|
|
92
88
|
|
|
93
|
-
|
|
94
|
-
@d.stroke(line_color || data_row[DATA_COLOR_INDEX])
|
|
95
|
-
@d.stroke_width 5.0
|
|
96
|
-
|
|
89
|
+
store.data.each do |data_row|
|
|
97
90
|
x_offset = radius * Math.cos(current_angle)
|
|
98
91
|
y_offset = radius * Math.sin(current_angle)
|
|
99
92
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
center_y + y_offset)
|
|
93
|
+
Gruff::Renderer::Line.new(color: line_color || data_row.color, width: 5.0)
|
|
94
|
+
.render(center_x, center_y, center_x + x_offset, center_y + y_offset)
|
|
103
95
|
|
|
104
|
-
draw_label(center_x, center_y, current_angle, radius, data_row
|
|
96
|
+
draw_label(center_x, center_y, current_angle, radius, data_row.label.to_s) unless hide_text
|
|
105
97
|
|
|
106
98
|
current_angle += additive_angle
|
|
107
99
|
end
|
|
@@ -111,21 +103,16 @@ private
|
|
|
111
103
|
points = []
|
|
112
104
|
current_angle = rotation * Math::PI / 180.0
|
|
113
105
|
|
|
114
|
-
|
|
115
|
-
points << center_x + normalize_points(data_row
|
|
116
|
-
points << center_y + normalize_points(data_row
|
|
106
|
+
store.data.each do |data_row|
|
|
107
|
+
points << center_x + normalize_points(data_row.points.first) * Math.cos(current_angle)
|
|
108
|
+
points << center_y + normalize_points(data_row.points.first) * Math.sin(current_angle)
|
|
117
109
|
current_angle += additive_angle
|
|
118
110
|
end
|
|
119
111
|
|
|
120
|
-
|
|
121
|
-
@d.stroke(color || @marker_color)
|
|
122
|
-
@d.fill(color || @marker_color)
|
|
123
|
-
@d.fill_opacity 0.4
|
|
124
|
-
@d.polygon(*points)
|
|
112
|
+
Gruff::Renderer::Polygon.new(color: color || @marker_color, opacity: 0.4).render(points)
|
|
125
113
|
end
|
|
126
114
|
|
|
127
115
|
def sums_for_spider
|
|
128
|
-
|
|
116
|
+
store.data.reduce(0.0) { |sum, data_row| sum + data_row.points.first }
|
|
129
117
|
end
|
|
130
|
-
|
|
131
118
|
end
|
data/lib/gruff/stacked_area.rb
CHANGED
|
@@ -1,67 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
|
|
2
|
-
require
|
|
3
|
-
require
|
|
3
|
+
require 'gruff/base'
|
|
4
|
+
require 'gruff/helper/stacked_mixin'
|
|
4
5
|
|
|
6
|
+
#
|
|
7
|
+
# Here's how to set up a Gruff::StackedArea.
|
|
8
|
+
#
|
|
9
|
+
# g = Gruff::StackedArea.new
|
|
10
|
+
# g.title = 'StackedArea Graph'
|
|
11
|
+
# g.data :Jimmy, [25, 36, 86, 39, 25, 31, 79, 88]
|
|
12
|
+
# g.data :Charles, [80, 54, 67, 54, 68, 70, 90, 95]
|
|
13
|
+
# g.data :Julie, [22, 29, 35, 38, 36, 40, 46, 57]
|
|
14
|
+
# g.write('stacked_area.png')
|
|
15
|
+
#
|
|
5
16
|
class Gruff::StackedArea < Gruff::Base
|
|
6
17
|
include StackedMixin
|
|
7
18
|
attr_accessor :last_series_goes_on_bottom
|
|
8
|
-
|
|
19
|
+
|
|
9
20
|
def draw
|
|
10
|
-
|
|
21
|
+
calculate_maximum_by_stack
|
|
11
22
|
super
|
|
12
23
|
|
|
13
|
-
return unless
|
|
24
|
+
return unless data_given?
|
|
14
25
|
|
|
15
|
-
|
|
16
|
-
@d = @d.stroke 'transparent'
|
|
26
|
+
x_increment = @graph_width / (column_count - 1).to_f
|
|
17
27
|
|
|
18
|
-
height = Array.new(
|
|
28
|
+
height = Array.new(column_count, 0)
|
|
19
29
|
|
|
20
30
|
data_points = nil
|
|
21
31
|
iterator = last_series_goes_on_bottom ? :reverse_each : :each
|
|
22
|
-
|
|
32
|
+
store.norm_data.public_send(iterator) do |data_row|
|
|
23
33
|
prev_data_points = data_points
|
|
24
|
-
data_points =
|
|
25
|
-
|
|
26
|
-
@d = @d.fill data_row[DATA_COLOR_INDEX]
|
|
34
|
+
data_points = []
|
|
27
35
|
|
|
28
|
-
data_row
|
|
36
|
+
data_row.points.each_with_index do |data_point, index|
|
|
29
37
|
# Use incremented x and scaled y
|
|
30
|
-
new_x = @graph_left + (
|
|
38
|
+
new_x = @graph_left + (x_increment * index)
|
|
31
39
|
new_y = @graph_top + (@graph_height - data_point * @graph_height - height[index])
|
|
32
40
|
|
|
33
41
|
height[index] += (data_point * @graph_height)
|
|
34
|
-
|
|
42
|
+
|
|
35
43
|
data_points << new_x
|
|
36
44
|
data_points << new_y
|
|
37
|
-
|
|
38
|
-
draw_label(new_x, index)
|
|
39
45
|
|
|
46
|
+
draw_label(new_x, index)
|
|
40
47
|
end
|
|
41
48
|
|
|
49
|
+
poly_points = data_points.dup
|
|
42
50
|
if prev_data_points
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
poly_points << prev_data_points[2*i]
|
|
46
|
-
poly_points << prev_data_points[2*i+1]
|
|
51
|
+
(prev_data_points.length / 2 - 1).downto(0) do |i|
|
|
52
|
+
poly_points << prev_data_points[2 * i]
|
|
53
|
+
poly_points << prev_data_points[2 * i + 1]
|
|
47
54
|
end
|
|
48
|
-
poly_points << data_points[0]
|
|
49
|
-
poly_points << data_points[1]
|
|
50
55
|
else
|
|
51
|
-
poly_points = data_points.dup
|
|
52
56
|
poly_points << @graph_right
|
|
53
57
|
poly_points << @graph_bottom - 1
|
|
54
58
|
poly_points << @graph_left
|
|
55
59
|
poly_points << @graph_bottom - 1
|
|
56
|
-
poly_points << data_points[0]
|
|
57
|
-
poly_points << data_points[1]
|
|
58
60
|
end
|
|
59
|
-
|
|
61
|
+
poly_points << data_points[0]
|
|
62
|
+
poly_points << data_points[1]
|
|
60
63
|
|
|
64
|
+
Gruff::Renderer::Polygon.new(color: data_row.color).render(poly_points)
|
|
61
65
|
end
|
|
62
66
|
|
|
63
|
-
|
|
67
|
+
Gruff::Renderer.finish
|
|
64
68
|
end
|
|
65
|
-
|
|
66
|
-
|
|
67
69
|
end
|
data/lib/gruff/stacked_bar.rb
CHANGED
|
@@ -1,57 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
|
|
2
|
-
require
|
|
3
|
-
require
|
|
3
|
+
require 'gruff/base'
|
|
4
|
+
require 'gruff/helper/stacked_mixin'
|
|
5
|
+
require 'gruff/helper/bar_value_label_mixin'
|
|
4
6
|
|
|
7
|
+
#
|
|
8
|
+
# Here's how to set up a Gruff::StackedBar.
|
|
9
|
+
#
|
|
10
|
+
# g = Gruff::StackedBar.new
|
|
11
|
+
# g.title = 'StackedBar Graph'
|
|
12
|
+
# g.data :Art, [0, 5, 8, 15]
|
|
13
|
+
# g.data :Philosophy, [10, 3, 2, 8]
|
|
14
|
+
# g.data :Science, [2, 15, 8, 11]
|
|
15
|
+
# g.write('stacked_bar.png')
|
|
16
|
+
#
|
|
5
17
|
class Gruff::StackedBar < Gruff::Base
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
# Spacing factor applied between bars
|
|
9
|
-
attr_accessor :bar_spacing
|
|
10
|
-
|
|
11
|
-
# Draws a bar graph, but multiple sets are stacked on top of each other.
|
|
12
|
-
def draw
|
|
13
|
-
get_maximum_by_stack
|
|
14
|
-
super
|
|
15
|
-
return unless @has_data
|
|
16
|
-
|
|
17
|
-
# Setup spacing.
|
|
18
|
-
#
|
|
19
|
-
# Columns sit stacked.
|
|
20
|
-
@bar_spacing ||= 0.9
|
|
21
|
-
@bar_width = @graph_width / @column_count.to_f
|
|
22
|
-
padding = (@bar_width * (1 - @bar_spacing)) / 2
|
|
23
|
-
|
|
24
|
-
@d = @d.stroke_opacity 0.0
|
|
25
|
-
|
|
26
|
-
height = Array.new(@column_count, 0)
|
|
27
|
-
|
|
28
|
-
@norm_data.each_with_index do |data_row, row_index|
|
|
29
|
-
data_row[DATA_VALUES_INDEX].each_with_index do |data_point, point_index|
|
|
30
|
-
@d = @d.fill data_row[DATA_COLOR_INDEX]
|
|
31
|
-
|
|
32
|
-
# Calculate center based on bar_width and current row
|
|
33
|
-
label_center = @graph_left + (@bar_width * point_index) + (@bar_width * @bar_spacing / 2.0)
|
|
34
|
-
draw_label(label_center, point_index)
|
|
35
|
-
|
|
36
|
-
next if (data_point == 0)
|
|
37
|
-
# Use incremented x and scaled y
|
|
38
|
-
left_x = @graph_left + (@bar_width * point_index) + padding
|
|
39
|
-
left_y = @graph_top + (@graph_height -
|
|
40
|
-
data_point * @graph_height -
|
|
41
|
-
height[point_index]) + 1
|
|
42
|
-
right_x = left_x + @bar_width * @bar_spacing
|
|
43
|
-
right_y = @graph_top + @graph_height - height[point_index] - 1
|
|
44
|
-
|
|
45
|
-
# update the total height of the current stacked bar
|
|
46
|
-
height[point_index] += (data_point * @graph_height )
|
|
47
|
-
|
|
48
|
-
@d = @d.rectangle(left_x, left_y, right_x, right_y)
|
|
49
|
-
|
|
50
|
-
end
|
|
18
|
+
include StackedMixin
|
|
19
|
+
include BarValueLabelMixin
|
|
51
20
|
|
|
21
|
+
# Spacing factor applied between bars.
|
|
22
|
+
attr_accessor :bar_spacing
|
|
23
|
+
|
|
24
|
+
# Number of pixels between bar segments.
|
|
25
|
+
attr_accessor :segment_spacing
|
|
26
|
+
|
|
27
|
+
# Set the number output format for labels using sprintf.
|
|
28
|
+
# Default is +"%.2f"+.
|
|
29
|
+
attr_accessor :label_formatting
|
|
30
|
+
|
|
31
|
+
# Output the values for the bars on a bar graph.
|
|
32
|
+
# Default is +false+.
|
|
33
|
+
attr_accessor :show_labels_for_bar_values
|
|
34
|
+
|
|
35
|
+
def initialize_ivars
|
|
36
|
+
super
|
|
37
|
+
@label_formatting = nil
|
|
38
|
+
@show_labels_for_bar_values = false
|
|
39
|
+
end
|
|
40
|
+
private :initialize_ivars
|
|
41
|
+
|
|
42
|
+
# Draws a bar graph, but multiple sets are stacked on top of each other.
|
|
43
|
+
def draw
|
|
44
|
+
calculate_maximum_by_stack
|
|
45
|
+
super
|
|
46
|
+
return unless data_given?
|
|
47
|
+
|
|
48
|
+
# Setup spacing.
|
|
49
|
+
#
|
|
50
|
+
# Columns sit stacked.
|
|
51
|
+
@bar_spacing ||= 0.9
|
|
52
|
+
@segment_spacing ||= 2
|
|
53
|
+
|
|
54
|
+
bar_width = @graph_width / column_count.to_f
|
|
55
|
+
padding = (bar_width * (1 - @bar_spacing)) / 2
|
|
56
|
+
|
|
57
|
+
height = Array.new(column_count, 0)
|
|
58
|
+
bar_value_label = BarValueLabel.new(column_count, bar_width)
|
|
59
|
+
|
|
60
|
+
store.norm_data.each_with_index do |data_row, row_index|
|
|
61
|
+
data_row.points.each_with_index do |data_point, point_index|
|
|
62
|
+
next if data_point == 0
|
|
63
|
+
|
|
64
|
+
# Use incremented x and scaled y
|
|
65
|
+
left_x = @graph_left + (bar_width * point_index) + padding
|
|
66
|
+
left_y = @graph_top + (@graph_height -
|
|
67
|
+
data_point * @graph_height -
|
|
68
|
+
height[point_index]) + @segment_spacing
|
|
69
|
+
right_x = left_x + bar_width * @bar_spacing
|
|
70
|
+
right_y = @graph_top + @graph_height - height[point_index]
|
|
71
|
+
|
|
72
|
+
# update the total height of the current stacked bar
|
|
73
|
+
height[point_index] += (data_point * @graph_height)
|
|
74
|
+
|
|
75
|
+
rect_renderer = Gruff::Renderer::Rectangle.new(color: data_row.color)
|
|
76
|
+
rect_renderer.render(left_x, left_y, right_x, right_y)
|
|
77
|
+
|
|
78
|
+
# Calculate center based on bar_width and current row
|
|
79
|
+
label_center = left_x + bar_width * @bar_spacing / 2.0
|
|
80
|
+
draw_label(label_center, point_index)
|
|
81
|
+
|
|
82
|
+
bar_value_label.coordinates[point_index] = [left_x, left_y, right_x, right_y]
|
|
83
|
+
bar_value_label.values[point_index] += store.data[row_index].points[point_index]
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
if @show_labels_for_bar_values
|
|
88
|
+
bar_value_label.prepare_rendering(@label_formatting) do |x, y, text|
|
|
89
|
+
draw_value_label(x, y, text, true)
|
|
52
90
|
end
|
|
53
|
-
|
|
54
|
-
@d.draw(@base_image)
|
|
55
91
|
end
|
|
56
92
|
|
|
93
|
+
Gruff::Renderer.finish
|
|
94
|
+
end
|
|
57
95
|
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Gruff
|
|
4
|
+
# @private
|
|
5
|
+
class Store
|
|
6
|
+
class BaseData < Struct.new(:label, :points, :color)
|
|
7
|
+
def initialize(label, points, color)
|
|
8
|
+
self.label = label.to_s
|
|
9
|
+
self.points = Array(points)
|
|
10
|
+
self.color = color
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def columns
|
|
14
|
+
points.length
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def min
|
|
18
|
+
points.compact.min
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def max
|
|
22
|
+
points.compact.max
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def normalize(args = {})
|
|
26
|
+
norm_points = points.map do |point|
|
|
27
|
+
point.nil? ? nil : (point.to_f - args[:minimum].to_f) / args[:spread]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
self.class.new(label, norm_points, color)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Gruff
|
|
4
|
+
class Store
|
|
5
|
+
class CustomData < Struct.new(:label, :points, :color, :custom)
|
|
6
|
+
def initialize(label, points, color, custom = nil)
|
|
7
|
+
self.label = label.to_s
|
|
8
|
+
self.points = Array(points)
|
|
9
|
+
self.color = color
|
|
10
|
+
self.custom = custom
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def columns
|
|
14
|
+
points.length
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def min
|
|
18
|
+
points.compact.min
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def max
|
|
22
|
+
points.compact.max
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def normalize(args = {})
|
|
26
|
+
norm_points = points.map do |point|
|
|
27
|
+
point.nil? ? nil : (point.to_f - args[:minimum].to_f) / args[:spread]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
self.class.new(label, norm_points, color, custom)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|