gruff 0.13.0-java → 0.16.0-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +79 -0
- data/.rubocop.yml +29 -31
- data/CHANGELOG.md +43 -0
- data/README.md +11 -5
- data/gruff.gemspec +8 -10
- data/lib/gruff/accumulator_bar.rb +4 -2
- data/lib/gruff/area.rb +9 -12
- data/lib/gruff/bar.rb +46 -31
- data/lib/gruff/base.rb +236 -207
- data/lib/gruff/bezier.rb +6 -8
- data/lib/gruff/box_plot.rb +174 -0
- data/lib/gruff/bullet.rb +17 -16
- data/lib/gruff/candlestick.rb +112 -0
- data/lib/gruff/dot.rb +14 -14
- data/lib/gruff/font.rb +42 -0
- data/lib/gruff/helper/bar_conversion.rb +5 -5
- data/lib/gruff/helper/bar_value_label.rb +26 -20
- data/lib/gruff/helper/stacked_mixin.rb +4 -3
- data/lib/gruff/histogram.rb +9 -7
- data/lib/gruff/line.rb +96 -83
- data/lib/gruff/mini/bar.rb +9 -6
- data/lib/gruff/mini/legend.rb +16 -12
- data/lib/gruff/mini/pie.rb +9 -6
- data/lib/gruff/mini/side_bar.rb +9 -6
- data/lib/gruff/net.rb +16 -22
- data/lib/gruff/patch/rmagick.rb +0 -1
- data/lib/gruff/patch/string.rb +2 -1
- data/lib/gruff/pie.rb +42 -75
- data/lib/gruff/renderer/bezier.rb +8 -9
- data/lib/gruff/renderer/circle.rb +8 -9
- data/lib/gruff/renderer/dash_line.rb +10 -10
- data/lib/gruff/renderer/dot.rb +15 -14
- data/lib/gruff/renderer/ellipse.rb +8 -9
- data/lib/gruff/renderer/line.rb +8 -11
- data/lib/gruff/renderer/polygon.rb +9 -10
- data/lib/gruff/renderer/polyline.rb +8 -9
- data/lib/gruff/renderer/rectangle.rb +11 -8
- data/lib/gruff/renderer/renderer.rb +25 -40
- data/lib/gruff/renderer/text.rb +21 -37
- data/lib/gruff/scatter.rb +86 -85
- data/lib/gruff/side_bar.rb +50 -37
- data/lib/gruff/side_stacked_bar.rb +26 -35
- data/lib/gruff/spider.rb +52 -28
- data/lib/gruff/stacked_area.rb +20 -16
- data/lib/gruff/stacked_bar.rb +44 -22
- data/lib/gruff/store/store.rb +6 -10
- data/lib/gruff/store/xy_data.rb +2 -0
- data/lib/gruff/themes.rb +6 -6
- data/lib/gruff/version.rb +1 -1
- data/lib/gruff.rb +70 -57
- data/rails_generators/gruff/templates/controller.rb +1 -1
- metadata +15 -32
- data/.rubocop_todo.yml +0 -182
- data/.travis.yml +0 -23
- data/assets/plastik/blue.png +0 -0
- data/assets/plastik/green.png +0 -0
- data/assets/plastik/red.png +0 -0
- data/lib/gruff/photo_bar.rb +0 -93
- data/lib/gruff/scene.rb +0 -198
- data/lib/gruff/store/custom_data.rb +0 -36
data/lib/gruff/bezier.rb
CHANGED
@@ -19,12 +19,10 @@
|
|
19
19
|
# g.write('bezier.png')
|
20
20
|
#
|
21
21
|
class Gruff::Bezier < Gruff::Base
|
22
|
-
|
23
|
-
super
|
22
|
+
private
|
24
23
|
|
25
|
-
|
26
|
-
|
27
|
-
x_increment = @graph_width / (column_count - 1).to_f
|
24
|
+
def draw_graph
|
25
|
+
x_increment = @graph_width / (column_count - 1)
|
28
26
|
|
29
27
|
store.norm_data.each do |data_row|
|
30
28
|
poly_points = []
|
@@ -32,7 +30,7 @@ class Gruff::Bezier < Gruff::Base
|
|
32
30
|
data_row[1].each_with_index do |data_point, index|
|
33
31
|
# Use incremented x and scaled y
|
34
32
|
new_x = @graph_left + (x_increment * index)
|
35
|
-
new_y = @graph_top + (@graph_height - data_point * @graph_height)
|
33
|
+
new_y = @graph_top + (@graph_height - (data_point * @graph_height))
|
36
34
|
|
37
35
|
if index == 0 && RUBY_PLATFORM != 'java'
|
38
36
|
poly_points << new_x
|
@@ -48,9 +46,9 @@ class Gruff::Bezier < Gruff::Base
|
|
48
46
|
stroke_width = clip_value_if_greater_than(@columns / (store.norm_data.first[1].size * 4), 5.0)
|
49
47
|
|
50
48
|
if RUBY_PLATFORM == 'java'
|
51
|
-
Gruff::Renderer::Polyline.new(color: data_row.color, width: stroke_width).render(poly_points)
|
49
|
+
Gruff::Renderer::Polyline.new(renderer, color: data_row.color, width: stroke_width).render(poly_points)
|
52
50
|
else
|
53
|
-
Gruff::Renderer::Bezier.new(color: data_row.color, width: stroke_width).render(poly_points)
|
51
|
+
Gruff::Renderer::Bezier.new(renderer, color: data_row.color, width: stroke_width).render(poly_points)
|
54
52
|
end
|
55
53
|
end
|
56
54
|
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Here's how to set up a Gruff::BoxPlot.
|
5
|
+
#
|
6
|
+
# g = Gruff::BoxPlot.new
|
7
|
+
# g.data "A", [2, 3, 5, 6, 8, 10, 11, 15, 17, 20, 28, 29, 33, 34, 45, 46, 49, 61]
|
8
|
+
# g.data "B", [3, 4, 34, 35, 38, 39, 45, 60, 61, 69, 80, 130]
|
9
|
+
# g.data "C", [4, 40, 41, 46, 57, 64, 77, 76, 79, 78, 99, 153]
|
10
|
+
# g.write("box_plot.png")
|
11
|
+
#
|
12
|
+
class Gruff::BoxPlot < Gruff::Base
|
13
|
+
# Specifies the filling opacity in area graph. Default is +0.2+.
|
14
|
+
attr_writer :fill_opacity
|
15
|
+
|
16
|
+
# Specifies the stroke width in line. Default is +3.0+.
|
17
|
+
attr_writer :stroke_width
|
18
|
+
|
19
|
+
# Can be used to adjust the spaces between the bars.
|
20
|
+
# Accepts values between 0.00 and 1.00 where 0.00 means no spacing at all
|
21
|
+
# and 1 means that each bars' width is nearly 0 (so each bar is a simple
|
22
|
+
# line with no x dimension).
|
23
|
+
#
|
24
|
+
# Default value is +0.8+.
|
25
|
+
def spacing_factor=(space_percent)
|
26
|
+
raise ArgumentError, 'spacing_factor must be between 0.00 and 1.00' unless (space_percent >= 0) && (space_percent <= 1)
|
27
|
+
|
28
|
+
@spacing_factor = (1 - space_percent)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def initialize_attributes
|
34
|
+
super
|
35
|
+
@fill_opacity = 0.2
|
36
|
+
@stroke_width = 3.0
|
37
|
+
@spacing_factor = 0.8
|
38
|
+
end
|
39
|
+
|
40
|
+
def draw_graph
|
41
|
+
# Setup the BarConversion Object
|
42
|
+
conversion = Gruff::BarConversion.new(
|
43
|
+
top: @graph_top, bottom: @graph_bottom,
|
44
|
+
minimum_value: minimum_value, maximum_value: maximum_value, spread: @spread
|
45
|
+
)
|
46
|
+
|
47
|
+
width = (@graph_width - calculate_spacing) / normalized_boxes.size
|
48
|
+
bar_width = width * @spacing_factor
|
49
|
+
padding = width - bar_width
|
50
|
+
|
51
|
+
normalized_boxes.each_with_index do |box, index|
|
52
|
+
left_x = @graph_left + (width * index) + (padding / 2.0)
|
53
|
+
right_x = left_x + bar_width
|
54
|
+
center_x = (left_x + right_x) / 2.0
|
55
|
+
|
56
|
+
first_y, = conversion.get_top_bottom_scaled(box.first_quartile)
|
57
|
+
third_y, = conversion.get_top_bottom_scaled(box.third_quartile)
|
58
|
+
Gruff::Renderer::Rectangle.new(renderer, color: box.color, width: @stroke_width, opacity: @fill_opacity)
|
59
|
+
.render(left_x, first_y, right_x, third_y)
|
60
|
+
|
61
|
+
median_y, = conversion.get_top_bottom_scaled(box.median)
|
62
|
+
Gruff::Renderer::Line.new(renderer, color: box.color, width: @stroke_width * 2).render(left_x, median_y, right_x, median_y)
|
63
|
+
|
64
|
+
minmax_left_x = left_x + (bar_width / 4.0)
|
65
|
+
minmax_right_x = right_x - (bar_width / 4.0)
|
66
|
+
min_y, = conversion.get_top_bottom_scaled(box.lower_whisker)
|
67
|
+
Gruff::Renderer::Line.new(renderer, color: box.color, width: @stroke_width).render(minmax_left_x, min_y, minmax_right_x, min_y)
|
68
|
+
Gruff::Renderer::DashLine.new(renderer, color: box.color, width: @stroke_width, dasharray: [@stroke_width, @stroke_width * 2])
|
69
|
+
.render(center_x, min_y, center_x, first_y)
|
70
|
+
|
71
|
+
max_y, = conversion.get_top_bottom_scaled(box.upper_whisker)
|
72
|
+
Gruff::Renderer::Line.new(renderer, color: box.color, width: @stroke_width).render(minmax_left_x, max_y, minmax_right_x, max_y)
|
73
|
+
Gruff::Renderer::DashLine.new(renderer, color: box.color, width: @stroke_width, dasharray: [@stroke_width, @stroke_width * 2])
|
74
|
+
.render(center_x, max_y, center_x, third_y)
|
75
|
+
|
76
|
+
box.lower_outliers.each do |outlier|
|
77
|
+
outlier_y, = conversion.get_top_bottom_scaled(outlier)
|
78
|
+
Gruff::Renderer::Dot.new(renderer, :circle, color: box.color, opacity: @fill_opacity).render(center_x, outlier_y, @stroke_width * 2)
|
79
|
+
end
|
80
|
+
|
81
|
+
box.upper_outliers.each do |outlier|
|
82
|
+
outlier_y, = conversion.get_top_bottom_scaled(outlier)
|
83
|
+
Gruff::Renderer::Dot.new(renderer, :circle, color: box.color, opacity: @fill_opacity).render(center_x, outlier_y, @stroke_width * 2)
|
84
|
+
end
|
85
|
+
|
86
|
+
draw_label(center_x, index)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def normalized_boxes
|
91
|
+
@normalized_boxes ||= store.norm_data.map { |data| Gruff::BoxPlot::BoxData.new(data.label, data.points, data.color) }
|
92
|
+
end
|
93
|
+
|
94
|
+
def calculate_spacing
|
95
|
+
@scale * (column_count - 1)
|
96
|
+
end
|
97
|
+
|
98
|
+
# @private
|
99
|
+
class BoxData < Struct.new(:label, :points, :color)
|
100
|
+
def initialize(label, points, color)
|
101
|
+
super(label, points.sort, color)
|
102
|
+
end
|
103
|
+
|
104
|
+
def min
|
105
|
+
points.first || 0
|
106
|
+
end
|
107
|
+
|
108
|
+
def max
|
109
|
+
points.last || 0
|
110
|
+
end
|
111
|
+
|
112
|
+
def min_whisker
|
113
|
+
[min, first_quartile - (1.5 * interquartile_range)].max
|
114
|
+
end
|
115
|
+
|
116
|
+
def max_whisker
|
117
|
+
[max, third_quartile + (1.5 * interquartile_range)].min
|
118
|
+
end
|
119
|
+
|
120
|
+
def upper_whisker
|
121
|
+
max = max_whisker
|
122
|
+
points.select { |point| point <= max }.max
|
123
|
+
end
|
124
|
+
|
125
|
+
def lower_whisker
|
126
|
+
min = min_whisker
|
127
|
+
points.select { |point| point >= min }.min
|
128
|
+
end
|
129
|
+
|
130
|
+
def median
|
131
|
+
if points.size.zero?
|
132
|
+
0
|
133
|
+
elsif points.size.odd?
|
134
|
+
points[points.size / 2]
|
135
|
+
else
|
136
|
+
(points[points.size / 2] + points[(points.size / 2) - 1]) / 2.0
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def first_quartile
|
141
|
+
if points.size.zero?
|
142
|
+
0
|
143
|
+
elsif points.size.odd?
|
144
|
+
points[points.size / 4]
|
145
|
+
else
|
146
|
+
(points[points.size / 4] + points[(points.size / 4) - 1]) / 2.0
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def third_quartile
|
151
|
+
if points.size.zero?
|
152
|
+
0
|
153
|
+
elsif points.size.odd?
|
154
|
+
points[(points.size * 3) / 4]
|
155
|
+
else
|
156
|
+
(points[(points.size * 3) / 4] + points[((points.size * 3) / 4) - 1]) / 2.0
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def lower_outliers
|
161
|
+
min = lower_whisker
|
162
|
+
points.select { |point| point < min }
|
163
|
+
end
|
164
|
+
|
165
|
+
def upper_outliers
|
166
|
+
max = upper_whisker
|
167
|
+
points.select { |point| point > max }
|
168
|
+
end
|
169
|
+
|
170
|
+
def interquartile_range
|
171
|
+
third_quartile - first_quartile
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
data/lib/gruff/bullet.rb
CHANGED
@@ -27,12 +27,13 @@ class Gruff::Bullet < Gruff::Base
|
|
27
27
|
self.theme = Gruff::Themes::GREYSCALE
|
28
28
|
end
|
29
29
|
|
30
|
-
def
|
30
|
+
def initialize_attributes
|
31
31
|
super
|
32
32
|
|
33
|
-
@
|
33
|
+
@title_font.size = 20
|
34
|
+
@title_font.bold = false
|
34
35
|
end
|
35
|
-
private :
|
36
|
+
private :initialize_attributes
|
36
37
|
|
37
38
|
def data(value, maximum_value, options = {})
|
38
39
|
@value = value.to_f
|
@@ -51,7 +52,7 @@ class Gruff::Bullet < Gruff::Base
|
|
51
52
|
|
52
53
|
draw_title
|
53
54
|
|
54
|
-
title_width = calculate_width(@
|
55
|
+
title_width = calculate_width(@title_font, @title)
|
55
56
|
margin = 30.0
|
56
57
|
thickness = @raw_rows / 6.0
|
57
58
|
right_margin = margin
|
@@ -60,29 +61,29 @@ class Gruff::Bullet < Gruff::Base
|
|
60
61
|
graph_height = thickness * 3.0
|
61
62
|
|
62
63
|
# Background
|
63
|
-
rect_renderer = Gruff::Renderer::Rectangle.new(color: @colors[0])
|
64
|
+
rect_renderer = Gruff::Renderer::Rectangle.new(renderer, color: @colors[0])
|
64
65
|
rect_renderer.render(graph_left, 0, graph_left + graph_width, graph_height)
|
65
66
|
|
66
|
-
[
|
67
|
+
%i[high low].each_with_index do |indicator, index|
|
67
68
|
next unless @options.key?(indicator)
|
68
69
|
|
69
|
-
indicator_width_x = graph_left + graph_width * (@options[indicator] / maximum_value)
|
70
|
+
indicator_width_x = graph_left + (graph_width * (@options[indicator] / maximum_value))
|
70
71
|
|
71
|
-
rect_renderer = Gruff::Renderer::Rectangle.new(color: @colors[index + 1])
|
72
|
+
rect_renderer = Gruff::Renderer::Rectangle.new(renderer, color: @colors[index + 1])
|
72
73
|
rect_renderer.render(graph_left, 0, indicator_width_x, graph_height)
|
73
74
|
end
|
74
75
|
|
75
76
|
if @options.key?(:target)
|
76
|
-
target_x = graph_left + graph_width * (@options[:target] / maximum_value)
|
77
|
+
target_x = graph_left + (graph_width * (@options[:target] / maximum_value))
|
77
78
|
half_thickness = thickness / 2.0
|
78
79
|
|
79
|
-
rect_renderer = Gruff::Renderer::Rectangle.new(color: @
|
80
|
-
rect_renderer.render(target_x, half_thickness, target_x + half_thickness, thickness * 2 + half_thickness)
|
80
|
+
rect_renderer = Gruff::Renderer::Rectangle.new(renderer, color: @marker_color)
|
81
|
+
rect_renderer.render(target_x, half_thickness, target_x + half_thickness, (thickness * 2) + half_thickness)
|
81
82
|
end
|
82
83
|
|
83
84
|
# Value
|
84
|
-
rect_renderer = Gruff::Renderer::Rectangle.new(color: @
|
85
|
-
rect_renderer.render(graph_left, thickness, graph_left + graph_width * (@value / maximum_value), thickness * 2)
|
85
|
+
rect_renderer = Gruff::Renderer::Rectangle.new(renderer, color: @marker_color)
|
86
|
+
rect_renderer.render(graph_left, thickness, graph_left + (graph_width * (@value / maximum_value)), thickness * 2)
|
86
87
|
end
|
87
88
|
|
88
89
|
private
|
@@ -90,9 +91,9 @@ private
|
|
90
91
|
def draw_title
|
91
92
|
return if hide_title?
|
92
93
|
|
93
|
-
font_height = calculate_caps_height(
|
94
|
+
font_height = calculate_caps_height(@title_font)
|
94
95
|
|
95
|
-
text_renderer = Gruff::Renderer::Text.new(@title, font: @
|
96
|
-
text_renderer.add_to_render_queue(1.0, 1.0, font_height / 2, font_height / 2, Magick::NorthWestGravity)
|
96
|
+
text_renderer = Gruff::Renderer::Text.new(renderer, @title, font: @title_font)
|
97
|
+
text_renderer.add_to_render_queue(1.0, 1.0, font_height / 2.0, font_height / 2.0, Magick::NorthWestGravity)
|
97
98
|
end
|
98
99
|
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Here's how to set up a Gruff::Candlestick.
|
5
|
+
#
|
6
|
+
# g = Gruff::Candlestick.new
|
7
|
+
# g.data low: 79.30, high: 93.10, open: 79.44, close: 91.20
|
8
|
+
# g.data low: 89.14, high: 106.42, open: 91.28, close: 106.26
|
9
|
+
# g.data low: 107.89, high: 131.00, open: 108.20, close: 129.04
|
10
|
+
# g.data low: 103.10, high: 137.98, open: 132.76, close: 115.81
|
11
|
+
# g.write("candlestick.png")
|
12
|
+
#
|
13
|
+
class Gruff::Candlestick < Gruff::Base
|
14
|
+
# Allow for vertical marker lines.
|
15
|
+
attr_writer :show_vertical_markers
|
16
|
+
|
17
|
+
# Specifies the filling opacity in area graph. Default is +0.4+.
|
18
|
+
attr_writer :fill_opacity
|
19
|
+
|
20
|
+
# Specifies the stroke width in line. Default is +2.0+.
|
21
|
+
attr_writer :stroke_width
|
22
|
+
|
23
|
+
# Specifies the color with up bar. Default is +'#579773'+.
|
24
|
+
attr_writer :up_color
|
25
|
+
|
26
|
+
# Specifies the color with down bar. Default is +'#eb5242'+.
|
27
|
+
attr_writer :down_color
|
28
|
+
|
29
|
+
# Can be used to adjust the spaces between the bars.
|
30
|
+
# Accepts values between 0.00 and 1.00 where 0.00 means no spacing at all
|
31
|
+
# and 1 means that each bars' width is nearly 0 (so each bar is a simple
|
32
|
+
# line with no x dimension).
|
33
|
+
#
|
34
|
+
# Default value is +0.9+.
|
35
|
+
def spacing_factor=(space_percent)
|
36
|
+
raise ArgumentError, 'spacing_factor must be between 0.00 and 1.00' unless (space_percent >= 0) && (space_percent <= 1)
|
37
|
+
|
38
|
+
@spacing_factor = (1 - space_percent)
|
39
|
+
end
|
40
|
+
|
41
|
+
def data(low:, high:, open:, close:)
|
42
|
+
super('', [low, high, open, close])
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def initialize_attributes
|
48
|
+
super
|
49
|
+
@show_vertical_markers = false
|
50
|
+
@fill_opacity = 0.4
|
51
|
+
@spacing_factor = 0.9
|
52
|
+
@stroke_width = 2.0
|
53
|
+
@up_color = '#579773'
|
54
|
+
@down_color = '#eb5242'
|
55
|
+
|
56
|
+
@sort = false
|
57
|
+
@hide_legend = true
|
58
|
+
end
|
59
|
+
|
60
|
+
def draw_graph
|
61
|
+
# Setup the BarConversion Object
|
62
|
+
conversion = Gruff::BarConversion.new(
|
63
|
+
top: @graph_top, bottom: @graph_bottom,
|
64
|
+
minimum_value: minimum_value, maximum_value: maximum_value, spread: @spread
|
65
|
+
)
|
66
|
+
|
67
|
+
width = (@graph_width - calculate_spacing) / normalized_candlesticks.size
|
68
|
+
bar_width = width * @spacing_factor
|
69
|
+
padding = width - bar_width
|
70
|
+
|
71
|
+
normalized_candlesticks.each_with_index do |candlestick, index|
|
72
|
+
left_x = @graph_left + (width * index) + (padding / 2.0)
|
73
|
+
right_x = left_x + bar_width
|
74
|
+
center_x = (left_x + right_x) / 2.0
|
75
|
+
color = candlestick.close >= candlestick.open ? @up_color : @down_color
|
76
|
+
|
77
|
+
draw_label(center_x, index) do
|
78
|
+
break if @hide_line_markers
|
79
|
+
break unless @show_vertical_markers
|
80
|
+
|
81
|
+
Gruff::Renderer::Line.new(renderer, color: @marker_color).render(center_x, @graph_bottom, center_x, @graph_top)
|
82
|
+
if @marker_shadow_color
|
83
|
+
Gruff::Renderer::Line.new(renderer, color: @marker_shadow_color)
|
84
|
+
.render(center_x + 1, @graph_bottom, center_x + 1, @graph_top)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
open_y, = conversion.get_top_bottom_scaled(candlestick.open)
|
89
|
+
close_y, = conversion.get_top_bottom_scaled(candlestick.close)
|
90
|
+
Gruff::Renderer::Rectangle.new(renderer, color: color, opacity: @fill_opacity, width: @stroke_width).render(left_x, open_y, right_x, close_y)
|
91
|
+
|
92
|
+
low_y, = conversion.get_top_bottom_scaled(candlestick.low)
|
93
|
+
y = open_y < close_y ? close_y : open_y
|
94
|
+
Gruff::Renderer::Line.new(renderer, color: color, width: @stroke_width).render(center_x, low_y, center_x, y)
|
95
|
+
high_y, = conversion.get_top_bottom_scaled(candlestick.high)
|
96
|
+
y = open_y > close_y ? close_y : open_y
|
97
|
+
Gruff::Renderer::Line.new(renderer, color: color, width: @stroke_width).render(center_x, y, center_x, high_y)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def normalized_candlesticks
|
102
|
+
@candlesticks ||= store.norm_data.map { |data| Gruff::Candlestick::CandlestickData.new(*data.points) }
|
103
|
+
end
|
104
|
+
|
105
|
+
def calculate_spacing
|
106
|
+
@scale * (column_count - 1)
|
107
|
+
end
|
108
|
+
|
109
|
+
# @private
|
110
|
+
class CandlestickData < Struct.new(:low, :high, :open, :close)
|
111
|
+
end
|
112
|
+
end
|
data/lib/gruff/dot.rb
CHANGED
@@ -14,52 +14,52 @@
|
|
14
14
|
# g.write('dot.png')
|
15
15
|
#
|
16
16
|
class Gruff::Dot < Gruff::Base
|
17
|
-
def
|
18
|
-
@has_left_labels = true
|
17
|
+
def initialize(*)
|
19
18
|
super
|
19
|
+
@has_left_labels = true
|
20
|
+
end
|
20
21
|
|
21
|
-
|
22
|
+
private
|
22
23
|
|
24
|
+
def draw_graph
|
23
25
|
# Setup spacing.
|
24
26
|
#
|
25
27
|
spacing_factor = 1.0
|
26
28
|
|
27
|
-
items_width = @graph_height / column_count
|
29
|
+
items_width = @graph_height / column_count
|
28
30
|
item_width = items_width * spacing_factor / store.length
|
29
31
|
padding = (items_width * (1 - spacing_factor)) / 2
|
30
32
|
|
31
33
|
store.norm_data.each_with_index do |data_row, row_index|
|
32
34
|
data_row.points.each_with_index do |data_point, point_index|
|
33
35
|
x_pos = @graph_left + (data_point * @graph_width)
|
34
|
-
y_pos = @graph_top + (items_width * point_index) + padding + (items_width
|
36
|
+
y_pos = @graph_top + (items_width * point_index) + padding + (items_width / 2.0)
|
35
37
|
|
36
38
|
if row_index == 0
|
37
|
-
Gruff::Renderer::Line.new(color: @marker_color).render(@graph_left, y_pos, @graph_left + @graph_width, y_pos)
|
39
|
+
Gruff::Renderer::Line.new(renderer, color: @marker_color).render(@graph_left, y_pos, @graph_left + @graph_width, y_pos)
|
38
40
|
end
|
39
41
|
|
40
|
-
Gruff::Renderer::Circle.new(color: data_row.color).render(x_pos, y_pos, x_pos + (item_width
|
42
|
+
Gruff::Renderer::Circle.new(renderer, color: data_row.color).render(x_pos, y_pos, x_pos + (item_width / 3.0), y_pos)
|
41
43
|
|
42
44
|
draw_label(y_pos, point_index)
|
43
45
|
end
|
44
46
|
end
|
45
47
|
end
|
46
48
|
|
47
|
-
protected
|
48
|
-
|
49
49
|
# Instead of base class version, draws vertical background lines and label
|
50
50
|
def draw_line_markers
|
51
51
|
return if @hide_line_markers
|
52
52
|
|
53
53
|
(0..marker_count).each do |index|
|
54
|
-
marker_label = BigDecimal(index.to_s) * BigDecimal(@increment.to_s) + BigDecimal(minimum_value.to_s)
|
55
|
-
x = @graph_left + (marker_label - minimum_value) * @graph_width / @spread
|
54
|
+
marker_label = (BigDecimal(index.to_s) * BigDecimal(@increment.to_s)) + BigDecimal(minimum_value.to_s)
|
55
|
+
x = @graph_left + ((marker_label - minimum_value) * @graph_width / @spread)
|
56
56
|
|
57
|
-
|
58
|
-
|
57
|
+
Gruff::Renderer::Line.new(renderer, color: @marker_color).render(x, @graph_bottom, x, @graph_bottom + 5)
|
58
|
+
Gruff::Renderer::Line.new(renderer, color: @marker_shadow_color).render(x, @graph_bottom + 1, x, @graph_bottom + 6) if @marker_shadow_color
|
59
59
|
|
60
60
|
unless @hide_line_numbers
|
61
61
|
label = y_axis_label(marker_label, @increment)
|
62
|
-
text_renderer = Gruff::Renderer::Text.new(label, font: @
|
62
|
+
text_renderer = Gruff::Renderer::Text.new(renderer, label, font: @marker_font)
|
63
63
|
text_renderer.add_to_render_queue(0, 0, x, @graph_bottom + (LABEL_MARGIN * 1.5), Magick::CenterGravity)
|
64
64
|
end
|
65
65
|
end
|
data/lib/gruff/font.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Handle font setting to draw texts
|
4
|
+
class Gruff::Font
|
5
|
+
BOLD_PATH = File.expand_path(File.join(__FILE__, '../../../assets/fonts/Roboto-Bold.ttf')).freeze
|
6
|
+
private_constant :BOLD_PATH
|
7
|
+
|
8
|
+
REGULAR_PATH = File.expand_path(File.join(__FILE__, '../../../assets/fonts/Roboto-Regular.ttf')).freeze
|
9
|
+
private_constant :REGULAR_PATH
|
10
|
+
|
11
|
+
# Get/set font path.
|
12
|
+
attr_accessor :path
|
13
|
+
|
14
|
+
# Get/set font size.
|
15
|
+
attr_accessor :size
|
16
|
+
|
17
|
+
# Get/set font setting whether render bold text.
|
18
|
+
attr_accessor :bold
|
19
|
+
|
20
|
+
# Get/set font color.
|
21
|
+
attr_accessor :color
|
22
|
+
|
23
|
+
def initialize(path: nil, size: 20.0, bold: false, color: 'white')
|
24
|
+
@path = path
|
25
|
+
@bold = bold
|
26
|
+
@size = size
|
27
|
+
@color = color
|
28
|
+
end
|
29
|
+
|
30
|
+
# Get font weight.
|
31
|
+
# @return [Magick::WeightType] font weight
|
32
|
+
def weight
|
33
|
+
@bold ? Magick::BoldWeight : Magick::NormalWeight
|
34
|
+
end
|
35
|
+
|
36
|
+
# @private
|
37
|
+
def file_path
|
38
|
+
return @path if @path
|
39
|
+
|
40
|
+
@bold ? BOLD_PATH : REGULAR_PATH
|
41
|
+
end
|
42
|
+
end
|
@@ -41,17 +41,17 @@ class Gruff::BarConversion
|
|
41
41
|
case @mode
|
42
42
|
when 1
|
43
43
|
# minimum value >= 0 ( only positive values )
|
44
|
-
result[0] = @graph_top + @graph_height * (1 - data_point)
|
44
|
+
result[0] = @graph_top + (@graph_height * (1 - data_point))
|
45
45
|
result[1] = @graph_top + @graph_height
|
46
46
|
when 2
|
47
47
|
# only negative values
|
48
48
|
result[0] = @graph_top
|
49
|
-
result[1] = @graph_top + @graph_height * (1 - data_point)
|
49
|
+
result[1] = @graph_top + (@graph_height * (1 - data_point))
|
50
50
|
when 3
|
51
51
|
# positive and negative values
|
52
|
-
val = data_point - @minimum_value / @spread
|
53
|
-
result[0] = @graph_top + @graph_height * (1 - (val - @zero))
|
54
|
-
result[1] = @graph_top + @graph_height * (1 - @zero)
|
52
|
+
val = data_point - (@minimum_value / @spread)
|
53
|
+
result[0] = @graph_top + (@graph_height * (1 - (val - @zero)))
|
54
|
+
result[1] = @graph_top + (@graph_height * (1 - @zero))
|
55
55
|
else
|
56
56
|
result[0] = 0.0
|
57
57
|
result[1] = 0.0
|
@@ -4,6 +4,20 @@
|
|
4
4
|
module Gruff::BarValueLabel
|
5
5
|
using String::GruffCommify
|
6
6
|
|
7
|
+
# @private
|
8
|
+
def self.metrics(value, format, proc_text_metrics)
|
9
|
+
val = begin
|
10
|
+
if format.is_a?(Proc)
|
11
|
+
format.call(value)
|
12
|
+
else
|
13
|
+
sprintf(format || '%.2f', value).commify
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
metrics = proc_text_metrics.call(val)
|
18
|
+
[val, metrics]
|
19
|
+
end
|
20
|
+
|
7
21
|
# @private
|
8
22
|
class Base
|
9
23
|
attr_reader :coordinate, :value
|
@@ -16,31 +30,23 @@ module Gruff::BarValueLabel
|
|
16
30
|
|
17
31
|
# @private
|
18
32
|
class Bar < Base
|
19
|
-
def prepare_rendering(format,
|
20
|
-
left_x, left_y,
|
21
|
-
|
22
|
-
val = format.call(@value)
|
23
|
-
else
|
24
|
-
val = sprintf(format || '%.2f', @value).commify
|
25
|
-
end
|
33
|
+
def prepare_rendering(format, proc_text_metrics)
|
34
|
+
left_x, left_y, _right_x, _right_y = @coordinate
|
35
|
+
val, metrics = Gruff::BarValueLabel.metrics(@value, format, proc_text_metrics)
|
26
36
|
|
27
|
-
y = @value >= 0 ? left_y -
|
28
|
-
yield left_x
|
37
|
+
y = @value >= 0 ? left_y - metrics.height - 5 : left_y + 5
|
38
|
+
yield left_x, y, val, metrics.width, metrics.height
|
29
39
|
end
|
30
40
|
end
|
31
41
|
|
32
42
|
# @private
|
33
43
|
class SideBar < Base
|
34
|
-
def prepare_rendering(format,
|
35
|
-
left_x,
|
36
|
-
|
37
|
-
val = format.call(@value)
|
38
|
-
else
|
39
|
-
val = sprintf(format || '%.2f', @value).commify
|
40
|
-
end
|
44
|
+
def prepare_rendering(format, proc_text_metrics)
|
45
|
+
left_x, left_y, right_x, _right_y = @coordinate
|
46
|
+
val, metrics = Gruff::BarValueLabel.metrics(@value, format, proc_text_metrics)
|
41
47
|
|
42
|
-
x = @value >= 0 ? right_x +
|
43
|
-
yield x,
|
48
|
+
x = @value >= 0 ? right_x + 10 : left_x - metrics.width - 10
|
49
|
+
yield x, left_y, val, metrics.width, metrics.height
|
44
50
|
end
|
45
51
|
end
|
46
52
|
|
@@ -56,12 +62,12 @@ module Gruff::BarValueLabel
|
|
56
62
|
@bars[index] = bars
|
57
63
|
end
|
58
64
|
|
59
|
-
def prepare_rendering(format,
|
65
|
+
def prepare_rendering(format, proc_text_metrics, &block)
|
60
66
|
@bars.each do |bars|
|
61
67
|
value = bars.sum(&:value)
|
62
68
|
bar = bars.last
|
63
69
|
bar_value_label = bar.class.new(bar.coordinate, value)
|
64
|
-
bar_value_label.prepare_rendering(format,
|
70
|
+
bar_value_label.prepare_rendering(format, proc_text_metrics, &block)
|
65
71
|
end
|
66
72
|
end
|
67
73
|
end
|
@@ -7,17 +7,18 @@ module Gruff::Base::StackedMixin
|
|
7
7
|
# tsal: moved from Base 03 FEB 2007
|
8
8
|
def calculate_maximum_by_stack
|
9
9
|
# Get sum of each stack
|
10
|
-
max_hash = {}
|
10
|
+
max_hash = Hash.new { |h, k| h[k] = 0.0 }
|
11
11
|
store.data.each do |data_set|
|
12
12
|
data_set.points.each_with_index do |data_point, i|
|
13
|
-
max_hash[i] = 0.0 unless max_hash[i]
|
14
13
|
max_hash[i] += data_point.to_f
|
15
14
|
end
|
16
15
|
end
|
17
16
|
|
18
17
|
max_hash.each_key do |key|
|
19
18
|
self.maximum_value = max_hash[key] if max_hash[key] > maximum_value
|
19
|
+
self.minimum_value = max_hash[key] if max_hash[key] < minimum_value
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
|
+
raise "Can't handle negative values in stacked graph" if minimum_value < 0
|
22
23
|
end
|
23
24
|
end
|
data/lib/gruff/histogram.rb
CHANGED
@@ -28,19 +28,21 @@ class Gruff::Histogram < Gruff::Bar
|
|
28
28
|
@data = []
|
29
29
|
end
|
30
30
|
|
31
|
-
def
|
31
|
+
def data(name, data_points = [], color = nil)
|
32
|
+
@data << [name, Array(data_points), color]
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def initialize_attributes
|
32
38
|
super
|
33
39
|
@bin_width = 10
|
34
40
|
@minimum_bin = nil
|
35
41
|
@maximum_bin = nil
|
36
42
|
end
|
37
|
-
private :
|
38
|
-
|
39
|
-
def data(name, data_points = [], color = nil)
|
40
|
-
@data << [name, data_points, color]
|
41
|
-
end
|
43
|
+
private :initialize_attributes
|
42
44
|
|
43
|
-
def
|
45
|
+
def setup_data
|
44
46
|
@data.each do |(name, data_points, color)|
|
45
47
|
bins, freqs = HistogramArray.new(data_points).histogram(bin_width: @bin_width, min: @minimum_bin, max: @maximum_bin)
|
46
48
|
bins.each_with_index do |bin, index|
|