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
data/lib/gruff/bezier.rb
CHANGED
@@ -1,7 +1,23 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
#
|
4
|
+
# Gruff::Bezier is a special line graph that have
|
5
|
+
# the bezier curve.
|
6
|
+
#
|
7
|
+
# Here's how to set up a Gruff::Bezier.
|
8
|
+
#
|
9
|
+
# dataset = [
|
10
|
+
# +0.00, +0.09, +0.19, +0.29, +0.38, +0.47, +0.56, +0.64, +0.71, +0.78,
|
11
|
+
# +0.84, +0.89, +0.93, +0.96, +0.98, +0.99, +0.99, +0.99, +0.97, +0.94,
|
12
|
+
# +0.90, +0.86, +0.80, +0.74, +0.67, +0.59, +0.51, +0.42, +0.33, +0.23,
|
13
|
+
# +0.14, +0.04, -0.06, -0.16, -0.26, -0.36, -0.45, -0.53, -0.62, -0.69,
|
14
|
+
# -0.76, -0.82, -0.88, -0.92, -0.96, -0.98, -1.00, -1.00, -1.00, -0.99,
|
15
|
+
# -0.96, -0.93, -0.89, -0.84, -0.78, -0.71, -0.64, -0.56, -0.47, -0.38,
|
16
|
+
# ]
|
17
|
+
# g = Gruff::Bezier.new
|
18
|
+
# g.data 'sin', dataset
|
19
|
+
# g.write('bezier.png')
|
20
|
+
#
|
5
21
|
class Gruff::Bezier < Gruff::Base
|
6
22
|
def draw
|
7
23
|
super
|
@@ -37,7 +53,5 @@ class Gruff::Bezier < Gruff::Base
|
|
37
53
|
Gruff::Renderer::Bezier.new(color: data_row.color, width: stroke_width).render(poly_points)
|
38
54
|
end
|
39
55
|
end
|
40
|
-
|
41
|
-
Gruff::Renderer.finish
|
42
56
|
end
|
43
57
|
end
|
data/lib/gruff/bullet.rb
CHANGED
@@ -1,15 +1,22 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
#
|
4
|
+
# A bullet graph is a variation of a bar graph.
|
6
5
|
# http://en.wikipedia.org/wiki/Bullet_graph
|
6
|
+
#
|
7
|
+
# Here's how to set up a Gruff::Bullet.
|
8
|
+
#
|
9
|
+
# g = Gruff::Bullet.new
|
10
|
+
# g.title = 'Monthly Revenue'
|
11
|
+
# g.data 75, 100, { target: 80, low: 50, high: 90 }
|
12
|
+
# g.write('bullet.png')
|
13
|
+
#
|
7
14
|
class Gruff::Bullet < Gruff::Base
|
8
15
|
def initialize(target_width = '400x40')
|
16
|
+
super
|
17
|
+
|
9
18
|
if target_width.is_a?(String)
|
10
|
-
|
11
|
-
@columns = geometric_width.to_f
|
12
|
-
@rows = geometric_height.to_f
|
19
|
+
@columns, @rows = target_width.split('x').map(&:to_f)
|
13
20
|
else
|
14
21
|
@columns = target_width.to_f
|
15
22
|
@rows = target_width.to_f / 5.0
|
@@ -17,12 +24,15 @@ class Gruff::Bullet < Gruff::Base
|
|
17
24
|
@columns.freeze
|
18
25
|
@rows.freeze
|
19
26
|
|
20
|
-
initialize_ivars
|
21
|
-
|
22
|
-
reset_themes
|
23
27
|
self.theme = Gruff::Themes::GREYSCALE
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize_ivars
|
31
|
+
super
|
32
|
+
|
24
33
|
@title_font_size = 20
|
25
34
|
end
|
35
|
+
private :initialize_ivars
|
26
36
|
|
27
37
|
def data(value, maximum_value, options = {})
|
28
38
|
@value = value.to_f
|
@@ -45,7 +55,7 @@ class Gruff::Bullet < Gruff::Base
|
|
45
55
|
margin = 30.0
|
46
56
|
thickness = @raw_rows / 6.0
|
47
57
|
right_margin = margin
|
48
|
-
graph_left =
|
58
|
+
graph_left = [title_width * 1.3, margin].max
|
49
59
|
graph_width = @raw_columns - graph_left - right_margin
|
50
60
|
graph_height = thickness * 3.0
|
51
61
|
|
@@ -73,18 +83,16 @@ class Gruff::Bullet < Gruff::Base
|
|
73
83
|
# Value
|
74
84
|
rect_renderer = Gruff::Renderer::Rectangle.new(color: @font_color)
|
75
85
|
rect_renderer.render(graph_left, thickness, graph_left + graph_width * (@value / maximum_value), thickness * 2)
|
76
|
-
|
77
|
-
Gruff::Renderer.finish
|
78
86
|
end
|
79
87
|
|
80
88
|
private
|
81
89
|
|
82
90
|
def draw_title
|
83
|
-
return
|
91
|
+
return if hide_title?
|
84
92
|
|
85
93
|
font_height = calculate_caps_height(scale_fontsize(@title_font_size))
|
86
94
|
|
87
95
|
text_renderer = Gruff::Renderer::Text.new(@title, font: @font, size: @title_font_size, color: @font_color)
|
88
|
-
text_renderer.
|
96
|
+
text_renderer.add_to_render_queue(1.0, 1.0, font_height / 2, font_height / 2, Magick::NorthWestGravity)
|
89
97
|
end
|
90
98
|
end
|
data/lib/gruff/dot.rb
CHANGED
@@ -1,9 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
# Graph with dots and labels along a vertical access
|
3
|
+
#
|
4
|
+
# Graph with dots and labels along a vertical access.
|
6
5
|
# see: 'Creating More Effective Graphs' by Robbins
|
6
|
+
#
|
7
|
+
# Here's how to set up a Gruff::Dot.
|
8
|
+
#
|
9
|
+
# g = Gruff::Dot.new
|
10
|
+
# g.title = 'Dot Graph'
|
11
|
+
# g.data :Art, [0, 5, 8, 15]
|
12
|
+
# g.data :Philosophy, [10, 3, 2, 8]
|
13
|
+
# g.data :Science, [2, 15, 8, 11]
|
14
|
+
# g.write('dot.png')
|
15
|
+
#
|
7
16
|
class Gruff::Dot < Gruff::Base
|
8
17
|
def draw
|
9
18
|
@has_left_labels = true
|
@@ -33,8 +42,6 @@ class Gruff::Dot < Gruff::Base
|
|
33
42
|
draw_label(y_pos, point_index)
|
34
43
|
end
|
35
44
|
end
|
36
|
-
|
37
|
-
Gruff::Renderer.finish
|
38
45
|
end
|
39
46
|
|
40
47
|
protected
|
@@ -43,37 +50,17 @@ protected
|
|
43
50
|
def draw_line_markers
|
44
51
|
return if @hide_line_markers
|
45
52
|
|
46
|
-
|
47
|
-
|
48
|
-
increment = @y_axis_increment
|
49
|
-
number_of_lines = (@spread / @y_axis_increment).to_i
|
50
|
-
else
|
51
|
-
# Try to use a number of horizontal lines that will come out even.
|
52
|
-
#
|
53
|
-
# TODO Do the same for larger numbers...100, 75, 50, 25
|
54
|
-
if @marker_count.nil?
|
55
|
-
(3..7).each do |lines|
|
56
|
-
if @spread % lines == 0.0
|
57
|
-
@marker_count = lines
|
58
|
-
break
|
59
|
-
end
|
60
|
-
end
|
61
|
-
@marker_count ||= 5
|
62
|
-
end
|
63
|
-
# TODO: Round maximum marker value to a round number like 100, 0.1, 0.5, etc.
|
64
|
-
increment = (@spread > 0 && @marker_count > 0) ? significant(@spread / @marker_count) : 1
|
65
|
-
number_of_lines = @marker_count
|
66
|
-
end
|
67
|
-
|
68
|
-
(0..number_of_lines).each do |index|
|
69
|
-
marker_label = minimum_value + index * increment
|
53
|
+
(0..marker_count).each do |index|
|
54
|
+
marker_label = minimum_value + index * @increment
|
70
55
|
x = @graph_left + (marker_label - minimum_value) * @graph_width / @spread
|
71
|
-
|
56
|
+
|
57
|
+
line_renderer = Gruff::Renderer::Line.new(color: @marker_color, shadow_color: @marker_shadow_color)
|
58
|
+
line_renderer.render(x, @graph_bottom, x, @graph_bottom + 5)
|
72
59
|
|
73
60
|
unless @hide_line_numbers
|
74
|
-
label = label(marker_label, increment)
|
61
|
+
label = label(marker_label, @increment)
|
75
62
|
text_renderer = Gruff::Renderer::Text.new(label, font: @font, size: @marker_font_size, color: @font_color)
|
76
|
-
text_renderer.
|
63
|
+
text_renderer.add_to_render_queue(0, 0, x, @graph_bottom + (LABEL_MARGIN * 1.5), Magick::CenterGravity)
|
77
64
|
end
|
78
65
|
end
|
79
66
|
end
|
@@ -84,7 +71,7 @@ protected
|
|
84
71
|
def draw_label(y_offset, index)
|
85
72
|
draw_unique_label(index) do
|
86
73
|
text_renderer = Gruff::Renderer::Text.new(@labels[index], font: @font, size: @marker_font_size, color: @font_color)
|
87
|
-
text_renderer.
|
74
|
+
text_renderer.add_to_render_queue(@graph_left - LABEL_MARGIN, 1.0, 0.0, y_offset, Magick::EastGravity)
|
88
75
|
end
|
89
76
|
end
|
90
77
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
##
|
4
4
|
# Original Author: David Stokar
|
5
5
|
#
|
6
|
-
# This class
|
6
|
+
# This class performs the y coordinates conversion for the bar class.
|
7
7
|
#
|
8
8
|
# There are three cases:
|
9
9
|
#
|
@@ -24,16 +24,16 @@ class Gruff::BarConversion
|
|
24
24
|
result = []
|
25
25
|
|
26
26
|
case @mode
|
27
|
-
when 1
|
28
|
-
|
27
|
+
when 1
|
28
|
+
# minimum value >= 0 ( only positive values )
|
29
29
|
result[0] = @graph_top + @graph_height * (1 - data_point) + 1
|
30
30
|
result[1] = @graph_top + @graph_height - 1
|
31
|
-
when 2
|
32
|
-
|
31
|
+
when 2
|
32
|
+
# only negative values
|
33
33
|
result[0] = @graph_top + 1
|
34
34
|
result[1] = @graph_top + @graph_height * (1 - data_point) - 1
|
35
|
-
when 3
|
36
|
-
|
35
|
+
when 3
|
36
|
+
# positive and negative values
|
37
37
|
val = data_point - @minimum_value / @spread
|
38
38
|
result[0] = @graph_top + @graph_height * (1 - (val - @zero)) + 1
|
39
39
|
result[1] = @graph_top + @graph_height * (1 - @zero) - 1
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'histogram'
|
4
|
+
|
5
|
+
#
|
6
|
+
# Here's how to set up a Gruff::Histogram.
|
7
|
+
#
|
8
|
+
# g = Gruff::Histogram.new
|
9
|
+
# g.title = 'Histogram Graph'
|
10
|
+
# g.minimum_bin = 10
|
11
|
+
# g.bin_width = 20
|
12
|
+
# g.data :A, [10, 10, 20, 30, 40, 40, 40, 40, 40, 40, 50, 10, 10, 10]
|
13
|
+
# g.data :B, [100, 100, 100, 100, 90, 90, 80, 30, 30, 30, 30, 30]
|
14
|
+
# g.write('histogram.png')
|
15
|
+
#
|
16
|
+
class Gruff::Histogram < Gruff::Bar
|
17
|
+
# Specifies interpolation between the min and max of the set. Default is +10+.
|
18
|
+
attr_writer :bin_width
|
19
|
+
|
20
|
+
# Specifies minimum value for bin.
|
21
|
+
attr_writer :minimum_bin
|
22
|
+
|
23
|
+
# Specifies maximum value for bin.
|
24
|
+
attr_writer :maximum_bin
|
25
|
+
|
26
|
+
def initialize(*)
|
27
|
+
super
|
28
|
+
@data = []
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize_ivars
|
32
|
+
super
|
33
|
+
@bin_width = 10
|
34
|
+
@minimum_bin = nil
|
35
|
+
@maximum_bin = nil
|
36
|
+
end
|
37
|
+
private :initialize_ivars
|
38
|
+
|
39
|
+
def data(name, data_points = [], color = nil)
|
40
|
+
@data << [name, data_points, color]
|
41
|
+
end
|
42
|
+
|
43
|
+
def draw
|
44
|
+
@data.each do |(name, data_points, color)|
|
45
|
+
bins, freqs = HistogramArray.new(data_points).histogram(bin_width: @bin_width, min: @minimum_bin, max: @maximum_bin)
|
46
|
+
bins.each_with_index do |bin, index|
|
47
|
+
@labels[index] = bin
|
48
|
+
end
|
49
|
+
store.add(name, freqs, color)
|
50
|
+
end
|
51
|
+
|
52
|
+
super
|
53
|
+
end
|
54
|
+
|
55
|
+
# @private
|
56
|
+
class HistogramArray < Array
|
57
|
+
include ::Histogram
|
58
|
+
end
|
59
|
+
end
|
data/lib/gruff/line.rb
CHANGED
@@ -1,41 +1,41 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
# Here's how to make a Line graph:
|
3
|
+
#
|
4
|
+
# Here's how to make a Gruff::Line.
|
6
5
|
#
|
7
6
|
# g = Gruff::Line.new
|
8
7
|
# g.title = "A Line Graph"
|
9
8
|
# g.data 'Fries', [20, 23, 19, 8]
|
10
9
|
# g.data 'Hamburgers', [50, 19, 99, 29]
|
11
|
-
# g.write("
|
10
|
+
# g.write("line.png")
|
12
11
|
#
|
13
12
|
# There are also other options described below, such as {#baseline_value}, {#baseline_color},
|
14
|
-
# {#hide_dots}, and {#hide_lines}.
|
13
|
+
# {#hide_dots=}, and {#hide_lines=}.
|
14
|
+
#
|
15
15
|
class Gruff::Line < Gruff::Base
|
16
16
|
# Allow for reference lines ( which are like baseline ... just allowing for more & on both axes ).
|
17
17
|
attr_accessor :reference_lines
|
18
|
-
|
19
|
-
|
18
|
+
attr_writer :reference_line_default_color
|
19
|
+
attr_writer :reference_line_default_width
|
20
20
|
|
21
21
|
# Allow for vertical marker lines.
|
22
|
-
|
22
|
+
attr_writer :show_vertical_markers
|
23
23
|
|
24
24
|
# Dimensions of lines and dots; calculated based on dataset size if left unspecified.
|
25
|
-
|
26
|
-
|
25
|
+
attr_writer :line_width
|
26
|
+
attr_writer :dot_radius
|
27
27
|
|
28
28
|
# default is +'circle'+, other options include square.
|
29
|
-
|
29
|
+
attr_writer :dot_style
|
30
30
|
|
31
|
-
# Hide parts of the graph to fit more
|
32
|
-
|
31
|
+
# Hide parts of the graph to fit more data points, or for a different appearance.
|
32
|
+
attr_writer :hide_dots, :hide_lines
|
33
33
|
|
34
34
|
# accessors for support of xy data.
|
35
|
-
|
35
|
+
attr_writer :minimum_x_value
|
36
36
|
|
37
37
|
# accessors for support of xy data.
|
38
|
-
|
38
|
+
attr_writer :maximum_x_value
|
39
39
|
|
40
40
|
# Get the value if somebody has defined it.
|
41
41
|
def baseline_value
|
@@ -67,7 +67,7 @@ class Gruff::Line < Gruff::Base
|
|
67
67
|
# g = Gruff::Line.new(400, false) # 400px wide, no lines (for backwards compatibility)
|
68
68
|
# g = Gruff::Line.new(false) # Defaults to 800px wide, no lines (for backwards compatibility)
|
69
69
|
#
|
70
|
-
# The preferred way is to call {#hide_dots} or {#hide_lines} instead.
|
70
|
+
# The preferred way is to call {#hide_dots=} or {#hide_lines=} instead.
|
71
71
|
def initialize(*args)
|
72
72
|
raise ArgumentError, 'Wrong number of arguments' if args.length > 2
|
73
73
|
|
@@ -76,7 +76,15 @@ class Gruff::Line < Gruff::Base
|
|
76
76
|
else
|
77
77
|
super args.shift
|
78
78
|
end
|
79
|
+
end
|
79
80
|
|
81
|
+
def initialize_store
|
82
|
+
@store = Gruff::Store.new(Gruff::Store::XYData)
|
83
|
+
end
|
84
|
+
private :initialize_store
|
85
|
+
|
86
|
+
def initialize_ivars
|
87
|
+
super
|
80
88
|
@reference_lines = {}
|
81
89
|
@reference_line_default_color = 'red'
|
82
90
|
@reference_line_default_width = 5
|
@@ -85,12 +93,13 @@ class Gruff::Line < Gruff::Base
|
|
85
93
|
@maximum_x_value = nil
|
86
94
|
@minimum_x_value = nil
|
87
95
|
|
96
|
+
@line_width = nil
|
97
|
+
@dot_radius = nil
|
88
98
|
@dot_style = 'circle'
|
89
99
|
|
90
100
|
@show_vertical_markers = false
|
91
|
-
|
92
|
-
@store = Gruff::Store.new(Gruff::Store::XYData)
|
93
101
|
end
|
102
|
+
private :initialize_ivars
|
94
103
|
|
95
104
|
# This method allows one to plot a dataset with both X and Y data.
|
96
105
|
#
|
@@ -123,7 +132,7 @@ class Gruff::Line < Gruff::Base
|
|
123
132
|
# g.data("Capples", [1, 1, 2, 2, 3, 3])
|
124
133
|
#
|
125
134
|
# # labels will be drawn at the x locations of the keys passed in.
|
126
|
-
# In this example the
|
135
|
+
# In this example the labels are drawn at x positions 2, 4, and 6:
|
127
136
|
# g.labels = {0 => '2003', 2 => '2004', 4 => '2005', 6 => '2006'}
|
128
137
|
# # The 0 => '2003' label will be ignored since it is outside the chart range.
|
129
138
|
def dataxy(name, x_data_points = [], y_data_points = [], color = nil)
|
@@ -144,11 +153,9 @@ class Gruff::Line < Gruff::Base
|
|
144
153
|
end
|
145
154
|
|
146
155
|
def draw_reference_line(reference_line, left, right, top, bottom)
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
}
|
151
|
-
Gruff::Renderer::DashLine.new(config).render(left, top, right, bottom)
|
156
|
+
color = reference_line[:color] || @reference_line_default_color
|
157
|
+
width = reference_line[:width] || @reference_line_default_width
|
158
|
+
Gruff::Renderer::DashLine.new(color: color, width: width).render(left, top, right, bottom)
|
152
159
|
end
|
153
160
|
|
154
161
|
def draw_horizontal_reference_line(reference_line)
|
@@ -210,8 +217,8 @@ class Gruff::Line < Gruff::Base
|
|
210
217
|
new_y = @graph_top + (@graph_height - y_data * @graph_height)
|
211
218
|
|
212
219
|
# Reset each time to avoid thin-line errors
|
213
|
-
stroke_width = line_width || clip_value_if_greater_than(@columns / (store.norm_data.first.y_points.size * 4), 5.0)
|
214
|
-
circle_radius = dot_radius || clip_value_if_greater_than(@columns / (store.norm_data.first.y_points.size * 2.5), 5.0)
|
220
|
+
stroke_width = @line_width || clip_value_if_greater_than(@columns / (store.norm_data.first.y_points.size * 4), 5.0)
|
221
|
+
circle_radius = @dot_radius || clip_value_if_greater_than(@columns / (store.norm_data.first.y_points.size * 2.5), 5.0)
|
215
222
|
|
216
223
|
if !@hide_lines && prev_x && prev_y
|
217
224
|
Gruff::Renderer::Line.new(color: data_row.color, width: stroke_width)
|
@@ -226,8 +233,6 @@ class Gruff::Line < Gruff::Base
|
|
226
233
|
prev_y = new_y
|
227
234
|
end
|
228
235
|
end
|
229
|
-
|
230
|
-
Gruff::Renderer.finish
|
231
236
|
end
|
232
237
|
|
233
238
|
private
|