gruff 0.13.0-java → 0.16.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 +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|
|