gruff 0.9.0 → 0.12.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +21 -4
- data/.rubocop_todo.yml +103 -49
- data/.travis.yml +3 -6
- data/CHANGELOG.md +30 -0
- data/README.md +4 -0
- data/gruff.gemspec +8 -3
- data/lib/gruff.rb +9 -3
- data/lib/gruff/accumulator_bar.rb +13 -5
- data/lib/gruff/area.rb +22 -5
- data/lib/gruff/bar.rb +38 -10
- data/lib/gruff/base.rb +325 -236
- data/lib/gruff/bezier.rb +18 -4
- data/lib/gruff/bullet.rb +22 -14
- data/lib/gruff/dot.rb +20 -33
- data/lib/gruff/helper/bar_conversion.rb +7 -7
- data/lib/gruff/helper/bar_value_label_mixin.rb +3 -0
- data/lib/gruff/helper/stacked_mixin.rb +1 -1
- data/lib/gruff/histogram.rb +59 -0
- data/lib/gruff/line.rb +33 -28
- data/lib/gruff/mini/bar.rb +10 -2
- data/lib/gruff/mini/legend.rb +9 -4
- data/lib/gruff/mini/pie.rb +9 -3
- data/lib/gruff/mini/side_bar.rb +18 -4
- data/lib/gruff/net.rb +41 -21
- data/lib/gruff/patch/rmagick.rb +22 -24
- data/lib/gruff/patch/string.rb +9 -4
- data/lib/gruff/photo_bar.rb +12 -16
- data/lib/gruff/pie.rb +24 -34
- data/lib/gruff/renderer/bezier.rb +4 -3
- data/lib/gruff/renderer/circle.rb +4 -3
- data/lib/gruff/renderer/dash_line.rb +4 -3
- data/lib/gruff/renderer/dot.rb +4 -3
- data/lib/gruff/renderer/ellipse.rb +4 -3
- data/lib/gruff/renderer/line.rb +14 -5
- data/lib/gruff/renderer/polygon.rb +5 -4
- data/lib/gruff/renderer/polyline.rb +4 -3
- data/lib/gruff/renderer/rectangle.rb +3 -2
- data/lib/gruff/renderer/renderer.rb +31 -38
- data/lib/gruff/renderer/text.rb +29 -7
- data/lib/gruff/scatter.rb +26 -24
- data/lib/gruff/scene.rb +0 -1
- data/lib/gruff/side_bar.rb +51 -20
- data/lib/gruff/side_stacked_bar.rb +42 -15
- data/lib/gruff/spider.rb +29 -17
- data/lib/gruff/stacked_area.rb +19 -8
- data/lib/gruff/stacked_bar.rb +34 -13
- data/lib/gruff/store/{base_data.rb → basic_data.rb} +9 -7
- data/lib/gruff/store/custom_data.rb +8 -6
- data/lib/gruff/store/store.rb +7 -6
- data/lib/gruff/store/xy_data.rb +10 -7
- data/lib/gruff/version.rb +1 -1
- metadata +50 -11
- data/Rakefile +0 -23
- data/docker/Dockerfile +0 -14
- data/docker/build.sh +0 -4
- data/docker/launch.sh +0 -4
@@ -1,52 +1,81 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
require 'gruff/helper/stacked_mixin'
|
5
|
-
|
3
|
+
#
|
6
4
|
# New gruff graph type added to enable sideways stacking bar charts
|
7
5
|
# (basically looks like a x/y flip of a standard stacking bar chart)
|
8
6
|
#
|
9
|
-
#
|
7
|
+
# Here's how to set up a Gruff::SideStackedBar.
|
8
|
+
#
|
9
|
+
# g = Gruff::SideStackedBar.new
|
10
|
+
# g.title = 'SideStackedBar Graph'
|
11
|
+
# g.labels = {
|
12
|
+
# 0 => '5/6',
|
13
|
+
# 1 => '5/15',
|
14
|
+
# 2 => '5/24',
|
15
|
+
# 3 => '5/30',
|
16
|
+
# }
|
17
|
+
# g.data :Art, [0, 5, 8, 15]
|
18
|
+
# g.data :Philosophy, [10, 3, 2, 8]
|
19
|
+
# g.data :Science, [2, 15, 8, 11]
|
20
|
+
# g.write('side_stacked_bar.png')
|
21
|
+
#
|
10
22
|
class Gruff::SideStackedBar < Gruff::SideBar
|
11
23
|
include StackedMixin
|
12
24
|
include BarValueLabelMixin
|
13
25
|
|
14
26
|
# Spacing factor applied between bars.
|
15
|
-
|
27
|
+
attr_writer :bar_spacing
|
16
28
|
|
17
29
|
# Number of pixels between bar segments.
|
18
|
-
|
30
|
+
attr_writer :segment_spacing
|
19
31
|
|
20
32
|
# Set the number output format for labels using sprintf.
|
21
33
|
# Default is +"%.2f"+.
|
22
|
-
|
34
|
+
attr_writer :label_formatting
|
23
35
|
|
24
36
|
# Output the values for the bars on a bar graph.
|
25
37
|
# Default is +false+.
|
26
|
-
|
38
|
+
attr_writer :show_labels_for_bar_values
|
39
|
+
|
40
|
+
# Prevent drawing of column labels left of a side stacked bar graph. Default is +false+.
|
41
|
+
attr_writer :hide_labels
|
27
42
|
|
28
43
|
def initialize_ivars
|
29
44
|
super
|
45
|
+
@bar_spacing = 0.9
|
46
|
+
@segment_spacing = 2.0
|
30
47
|
@label_formatting = nil
|
31
48
|
@show_labels_for_bar_values = false
|
49
|
+
@hide_labels = false
|
32
50
|
end
|
33
51
|
private :initialize_ivars
|
34
52
|
|
35
53
|
def draw
|
36
54
|
@has_left_labels = true
|
37
|
-
|
55
|
+
calculate_maximum_by_stack
|
38
56
|
super
|
39
57
|
end
|
40
58
|
|
59
|
+
protected
|
60
|
+
|
61
|
+
def hide_labels?
|
62
|
+
@hide_labels
|
63
|
+
end
|
64
|
+
|
65
|
+
def hide_left_label_area?
|
66
|
+
hide_labels?
|
67
|
+
end
|
68
|
+
|
69
|
+
def hide_bottom_label_area?
|
70
|
+
@hide_line_markers
|
71
|
+
end
|
72
|
+
|
41
73
|
private
|
42
74
|
|
43
75
|
def draw_bars
|
44
76
|
# Setup spacing.
|
45
77
|
#
|
46
78
|
# Columns sit stacked.
|
47
|
-
@bar_spacing ||= 0.9
|
48
|
-
@segment_spacing ||= 2.0
|
49
|
-
|
50
79
|
bar_width = @graph_height / column_count.to_f
|
51
80
|
height = Array.new(column_count, 0)
|
52
81
|
length = Array.new(column_count, @graph_left)
|
@@ -55,7 +84,7 @@ private
|
|
55
84
|
|
56
85
|
store.norm_data.each_with_index do |data_row, row_index|
|
57
86
|
data_row.points.each_with_index do |data_point, point_index|
|
58
|
-
## using the original
|
87
|
+
## using the original calculations from the stacked bar chart to get the difference between
|
59
88
|
## part of the bart chart we wish to stack.
|
60
89
|
temp1 = @graph_left + (@graph_width -
|
61
90
|
data_point * @graph_width -
|
@@ -93,7 +122,5 @@ private
|
|
93
122
|
draw_value_label(x, y, text, true)
|
94
123
|
end
|
95
124
|
end
|
96
|
-
|
97
|
-
Gruff::Renderer.finish
|
98
125
|
end
|
99
126
|
end
|
data/lib/gruff/spider.rb
CHANGED
@@ -1,16 +1,23 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'gruff/base'
|
4
|
-
|
5
3
|
# Experimental!!! See also the Net graph.
|
6
4
|
#
|
7
|
-
#
|
5
|
+
# Here's how to set up a Gruff::Spider.
|
6
|
+
#
|
7
|
+
# g = Gruff::Spider.new(30)
|
8
|
+
# g.title = "Spider Graph"
|
9
|
+
# g.data :Strength, [10]
|
10
|
+
# g.data :Dexterity, [16]
|
11
|
+
# g.data :Constitution, [12]
|
12
|
+
# g.data :Intelligence, [12]
|
13
|
+
# g.data :Wisdom, [10]
|
14
|
+
# g.data 'Charisma', [16]
|
15
|
+
# g.write("spider.png")
|
16
|
+
#
|
8
17
|
class Gruff::Spider < Gruff::Base
|
9
18
|
# Hide all text.
|
10
|
-
|
11
|
-
|
12
|
-
attr_reader :transparent_background
|
13
|
-
attr_accessor :rotation
|
19
|
+
attr_writer :hide_axes
|
20
|
+
attr_writer :rotation
|
14
21
|
|
15
22
|
def transparent_background=(value)
|
16
23
|
Gruff::Renderer.setup_transparent_background(@columns, @rows) if value
|
@@ -23,9 +30,16 @@ class Gruff::Spider < Gruff::Base
|
|
23
30
|
def initialize(max_value, target_width = 800)
|
24
31
|
super(target_width)
|
25
32
|
@max_value = max_value
|
33
|
+
end
|
34
|
+
|
35
|
+
def initialize_ivars
|
36
|
+
super
|
26
37
|
@hide_legend = true
|
38
|
+
@hide_axes = false
|
39
|
+
@hide_text = false
|
27
40
|
@rotation = 0
|
28
41
|
end
|
42
|
+
private :initialize_ivars
|
29
43
|
|
30
44
|
def draw
|
31
45
|
@hide_line_markers = true
|
@@ -44,12 +58,10 @@ class Gruff::Spider < Gruff::Base
|
|
44
58
|
additive_angle = (2 * Math::PI) / store.length
|
45
59
|
|
46
60
|
# Draw axes
|
47
|
-
draw_axes(center_x, center_y, radius, additive_angle) unless hide_axes
|
61
|
+
draw_axes(center_x, center_y, radius, additive_angle) unless @hide_axes
|
48
62
|
|
49
63
|
# Draw polygon
|
50
64
|
draw_polygon(center_x, center_y, additive_angle)
|
51
|
-
|
52
|
-
Gruff::Renderer.finish
|
53
65
|
end
|
54
66
|
|
55
67
|
private
|
@@ -66,14 +78,14 @@ private
|
|
66
78
|
y = y_offset + ((radius + r_offset) * Math.sin(angle))
|
67
79
|
|
68
80
|
# Draw label
|
69
|
-
text_renderer = Gruff::Renderer::Text.new(amount, font: @font, size: legend_font_size, color: @marker_color, weight: Magick::BoldWeight)
|
70
|
-
text_renderer.
|
81
|
+
text_renderer = Gruff::Renderer::Text.new(amount, font: @font, size: @legend_font_size, color: @marker_color, weight: Magick::BoldWeight)
|
82
|
+
text_renderer.add_to_render_queue(0, 0, x, y, Magick::CenterGravity)
|
71
83
|
end
|
72
84
|
|
73
85
|
def draw_axes(center_x, center_y, radius, additive_angle, line_color = nil)
|
74
|
-
return if hide_axes
|
86
|
+
return if @hide_axes
|
75
87
|
|
76
|
-
current_angle = rotation * Math::PI / 180.0
|
88
|
+
current_angle = @rotation * Math::PI / 180.0
|
77
89
|
|
78
90
|
store.data.each do |data_row|
|
79
91
|
x_offset = radius * Math.cos(current_angle)
|
@@ -82,7 +94,7 @@ private
|
|
82
94
|
Gruff::Renderer::Line.new(color: line_color || data_row.color, width: 5.0)
|
83
95
|
.render(center_x, center_y, center_x + x_offset, center_y + y_offset)
|
84
96
|
|
85
|
-
draw_label(center_x, center_y, current_angle, radius, data_row.label.to_s) unless hide_text
|
97
|
+
draw_label(center_x, center_y, current_angle, radius, data_row.label.to_s) unless @hide_text
|
86
98
|
|
87
99
|
current_angle += additive_angle
|
88
100
|
end
|
@@ -90,7 +102,7 @@ private
|
|
90
102
|
|
91
103
|
def draw_polygon(center_x, center_y, additive_angle, color = nil)
|
92
104
|
points = []
|
93
|
-
current_angle = rotation * Math::PI / 180.0
|
105
|
+
current_angle = @rotation * Math::PI / 180.0
|
94
106
|
|
95
107
|
store.data.each do |data_row|
|
96
108
|
points << center_x + normalize_points(data_row.points.first) * Math.cos(current_angle)
|
@@ -102,6 +114,6 @@ private
|
|
102
114
|
end
|
103
115
|
|
104
116
|
def sums_for_spider
|
105
|
-
store.data.
|
117
|
+
store.data.sum { |data_row| data_row.points.first }
|
106
118
|
end
|
107
119
|
end
|
data/lib/gruff/stacked_area.rb
CHANGED
@@ -1,14 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
#
|
4
|
+
# Here's how to set up a Gruff::StackedArea.
|
5
|
+
#
|
6
|
+
# g = Gruff::StackedArea.new
|
7
|
+
# g.title = 'StackedArea Graph'
|
8
|
+
# g.data :Jimmy, [25, 36, 86, 39, 25, 31, 79, 88]
|
9
|
+
# g.data :Charles, [80, 54, 67, 54, 68, 70, 90, 95]
|
10
|
+
# g.data :Julie, [22, 29, 35, 38, 36, 40, 46, 57]
|
11
|
+
# g.write('stacked_area.png')
|
12
|
+
#
|
6
13
|
class Gruff::StackedArea < Gruff::Base
|
7
14
|
include StackedMixin
|
8
|
-
|
15
|
+
attr_writer :last_series_goes_on_bottom
|
16
|
+
|
17
|
+
def initialize_ivars
|
18
|
+
super
|
19
|
+
@last_series_goes_on_bottom = false
|
20
|
+
end
|
21
|
+
private :initialize_ivars
|
9
22
|
|
10
23
|
def draw
|
11
|
-
|
24
|
+
calculate_maximum_by_stack
|
12
25
|
super
|
13
26
|
|
14
27
|
return unless data_given?
|
@@ -18,7 +31,7 @@ class Gruff::StackedArea < Gruff::Base
|
|
18
31
|
height = Array.new(column_count, 0)
|
19
32
|
|
20
33
|
data_points = nil
|
21
|
-
iterator = last_series_goes_on_bottom ? :reverse_each : :each
|
34
|
+
iterator = @last_series_goes_on_bottom ? :reverse_each : :each
|
22
35
|
store.norm_data.public_send(iterator) do |data_row|
|
23
36
|
prev_data_points = data_points
|
24
37
|
data_points = []
|
@@ -53,7 +66,5 @@ class Gruff::StackedArea < Gruff::Base
|
|
53
66
|
|
54
67
|
Gruff::Renderer::Polygon.new(color: data_row.color).render(poly_points)
|
55
68
|
end
|
56
|
-
|
57
|
-
Gruff::Renderer.finish
|
58
69
|
end
|
59
70
|
end
|
data/lib/gruff/stacked_bar.rb
CHANGED
@@ -1,46 +1,55 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
#
|
4
|
+
# Here's how to set up a Gruff::StackedBar.
|
5
|
+
#
|
6
|
+
# g = Gruff::StackedBar.new
|
7
|
+
# g.title = 'StackedBar Graph'
|
8
|
+
# g.data :Art, [0, 5, 8, 15]
|
9
|
+
# g.data :Philosophy, [10, 3, 2, 8]
|
10
|
+
# g.data :Science, [2, 15, 8, 11]
|
11
|
+
# g.write('stacked_bar.png')
|
12
|
+
#
|
7
13
|
class Gruff::StackedBar < Gruff::Base
|
8
14
|
include StackedMixin
|
9
15
|
include BarValueLabelMixin
|
10
16
|
|
11
17
|
# Spacing factor applied between bars.
|
12
|
-
|
18
|
+
attr_writer :bar_spacing
|
13
19
|
|
14
20
|
# Number of pixels between bar segments.
|
15
|
-
|
21
|
+
attr_writer :segment_spacing
|
16
22
|
|
17
23
|
# Set the number output format for labels using sprintf.
|
18
24
|
# Default is +"%.2f"+.
|
19
|
-
|
25
|
+
attr_writer :label_formatting
|
20
26
|
|
21
27
|
# Output the values for the bars on a bar graph.
|
22
28
|
# Default is +false+.
|
23
|
-
|
29
|
+
attr_writer :show_labels_for_bar_values
|
30
|
+
|
31
|
+
# Prevent drawing of column labels below a stacked bar graph. Default is +false+.
|
32
|
+
attr_writer :hide_labels
|
24
33
|
|
25
34
|
def initialize_ivars
|
26
35
|
super
|
36
|
+
@bar_spacing = 0.9
|
37
|
+
@segment_spacing = 2
|
27
38
|
@label_formatting = nil
|
28
39
|
@show_labels_for_bar_values = false
|
40
|
+
@hide_labels = false
|
29
41
|
end
|
30
42
|
private :initialize_ivars
|
31
43
|
|
32
44
|
# Draws a bar graph, but multiple sets are stacked on top of each other.
|
33
45
|
def draw
|
34
|
-
|
46
|
+
calculate_maximum_by_stack
|
35
47
|
super
|
36
48
|
return unless data_given?
|
37
49
|
|
38
50
|
# Setup spacing.
|
39
51
|
#
|
40
52
|
# Columns sit stacked.
|
41
|
-
@bar_spacing ||= 0.9
|
42
|
-
@segment_spacing ||= 2
|
43
|
-
|
44
53
|
bar_width = @graph_width / column_count.to_f
|
45
54
|
padding = (bar_width * (1 - @bar_spacing)) / 2
|
46
55
|
|
@@ -79,7 +88,19 @@ class Gruff::StackedBar < Gruff::Base
|
|
79
88
|
draw_value_label(x, y, text, true)
|
80
89
|
end
|
81
90
|
end
|
91
|
+
end
|
92
|
+
|
93
|
+
protected
|
94
|
+
|
95
|
+
def hide_labels?
|
96
|
+
@hide_labels
|
97
|
+
end
|
98
|
+
|
99
|
+
def hide_left_label_area?
|
100
|
+
@hide_line_markers
|
101
|
+
end
|
82
102
|
|
83
|
-
|
103
|
+
def hide_bottom_label_area?
|
104
|
+
hide_labels?
|
84
105
|
end
|
85
106
|
end
|
@@ -1,13 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Gruff
|
4
|
-
# @private
|
5
4
|
class Store
|
6
|
-
|
5
|
+
# @private
|
6
|
+
class BasicData < Struct.new(:label, :points, :color)
|
7
7
|
def initialize(label, points, color)
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
super(label.to_s, Array(points), color)
|
9
|
+
end
|
10
|
+
|
11
|
+
def empty?
|
12
|
+
points.empty?
|
11
13
|
end
|
12
14
|
|
13
15
|
def columns
|
@@ -22,9 +24,9 @@ module Gruff
|
|
22
24
|
points.compact.max
|
23
25
|
end
|
24
26
|
|
25
|
-
def normalize(
|
27
|
+
def normalize(minimum:, spread:)
|
26
28
|
norm_points = points.map do |point|
|
27
|
-
point.nil? ? nil : (point.to_f -
|
29
|
+
point.nil? ? nil : (point.to_f - minimum.to_f) / spread
|
28
30
|
end
|
29
31
|
|
30
32
|
self.class.new(label, norm_points, color)
|
@@ -2,12 +2,14 @@
|
|
2
2
|
|
3
3
|
module Gruff
|
4
4
|
class Store
|
5
|
+
# @private
|
5
6
|
class CustomData < Struct.new(:label, :points, :color, :custom)
|
6
7
|
def initialize(label, points, color, custom = nil)
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
super(label.to_s, Array(points), color, custom)
|
9
|
+
end
|
10
|
+
|
11
|
+
def empty?
|
12
|
+
points.empty?
|
11
13
|
end
|
12
14
|
|
13
15
|
def columns
|
@@ -22,9 +24,9 @@ module Gruff
|
|
22
24
|
points.compact.max
|
23
25
|
end
|
24
26
|
|
25
|
-
def normalize(
|
27
|
+
def normalize(minimum:, spread:)
|
26
28
|
norm_points = points.map do |point|
|
27
|
-
point.nil? ? nil : (point.to_f -
|
29
|
+
point.nil? ? nil : (point.to_f - minimum.to_f) / spread
|
28
30
|
end
|
29
31
|
|
30
32
|
self.class.new(label, norm_points, color, custom)
|
data/lib/gruff/store/store.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Gruff
|
4
|
+
# @private
|
4
5
|
class Store
|
5
6
|
attr_reader :data, :norm_data
|
6
7
|
|
@@ -15,10 +16,10 @@ module Gruff
|
|
15
16
|
@data << @data_class.new(*args)
|
16
17
|
end
|
17
18
|
|
18
|
-
def normalize(
|
19
|
+
def normalize(**keywords)
|
19
20
|
unless @normalized
|
20
21
|
@data.each do |data_row|
|
21
|
-
@norm_data << data_row.normalize(
|
22
|
+
@norm_data << data_row.normalize(**keywords)
|
22
23
|
end
|
23
24
|
|
24
25
|
@normalized = true
|
@@ -26,7 +27,7 @@ module Gruff
|
|
26
27
|
end
|
27
28
|
|
28
29
|
def empty?
|
29
|
-
@data.empty?
|
30
|
+
@data.all?(&:empty?)
|
30
31
|
end
|
31
32
|
|
32
33
|
def length
|
@@ -56,18 +57,18 @@ module Gruff
|
|
56
57
|
end
|
57
58
|
|
58
59
|
def sort_data!
|
59
|
-
@data = @data.sort_by { |a| -a.points.
|
60
|
+
@data = @data.sort_by { |a| -a.points.sum(&:to_f) }
|
60
61
|
end
|
61
62
|
|
62
63
|
def sort_norm_data!
|
63
|
-
@norm_data = @norm_data.sort_by { |a| -a.points.
|
64
|
+
@norm_data = @norm_data.sort_by { |a| -a.points.sum(&:to_f) }
|
64
65
|
end
|
65
66
|
|
66
67
|
def reverse!
|
67
68
|
@data.reverse!
|
68
69
|
end
|
69
70
|
|
70
|
-
def
|
71
|
+
def change_colors(colors)
|
71
72
|
index = 0
|
72
73
|
@data.each do |data_row|
|
73
74
|
data_row.color ||= begin
|