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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +79 -0
  3. data/.rubocop.yml +29 -31
  4. data/CHANGELOG.md +43 -0
  5. data/README.md +11 -5
  6. data/gruff.gemspec +8 -10
  7. data/lib/gruff/accumulator_bar.rb +4 -2
  8. data/lib/gruff/area.rb +9 -12
  9. data/lib/gruff/bar.rb +46 -31
  10. data/lib/gruff/base.rb +236 -207
  11. data/lib/gruff/bezier.rb +6 -8
  12. data/lib/gruff/box_plot.rb +174 -0
  13. data/lib/gruff/bullet.rb +17 -16
  14. data/lib/gruff/candlestick.rb +112 -0
  15. data/lib/gruff/dot.rb +14 -14
  16. data/lib/gruff/font.rb +42 -0
  17. data/lib/gruff/helper/bar_conversion.rb +5 -5
  18. data/lib/gruff/helper/bar_value_label.rb +26 -20
  19. data/lib/gruff/helper/stacked_mixin.rb +4 -3
  20. data/lib/gruff/histogram.rb +9 -7
  21. data/lib/gruff/line.rb +96 -83
  22. data/lib/gruff/mini/bar.rb +9 -6
  23. data/lib/gruff/mini/legend.rb +16 -12
  24. data/lib/gruff/mini/pie.rb +9 -6
  25. data/lib/gruff/mini/side_bar.rb +9 -6
  26. data/lib/gruff/net.rb +16 -22
  27. data/lib/gruff/patch/rmagick.rb +0 -1
  28. data/lib/gruff/patch/string.rb +2 -1
  29. data/lib/gruff/pie.rb +42 -75
  30. data/lib/gruff/renderer/bezier.rb +8 -9
  31. data/lib/gruff/renderer/circle.rb +8 -9
  32. data/lib/gruff/renderer/dash_line.rb +10 -10
  33. data/lib/gruff/renderer/dot.rb +15 -14
  34. data/lib/gruff/renderer/ellipse.rb +8 -9
  35. data/lib/gruff/renderer/line.rb +8 -11
  36. data/lib/gruff/renderer/polygon.rb +9 -10
  37. data/lib/gruff/renderer/polyline.rb +8 -9
  38. data/lib/gruff/renderer/rectangle.rb +11 -8
  39. data/lib/gruff/renderer/renderer.rb +25 -40
  40. data/lib/gruff/renderer/text.rb +21 -37
  41. data/lib/gruff/scatter.rb +86 -85
  42. data/lib/gruff/side_bar.rb +50 -37
  43. data/lib/gruff/side_stacked_bar.rb +26 -35
  44. data/lib/gruff/spider.rb +52 -28
  45. data/lib/gruff/stacked_area.rb +20 -16
  46. data/lib/gruff/stacked_bar.rb +44 -22
  47. data/lib/gruff/store/store.rb +6 -10
  48. data/lib/gruff/store/xy_data.rb +2 -0
  49. data/lib/gruff/themes.rb +6 -6
  50. data/lib/gruff/version.rb +1 -1
  51. data/lib/gruff.rb +70 -57
  52. data/rails_generators/gruff/templates/controller.rb +1 -1
  53. metadata +15 -32
  54. data/.rubocop_todo.yml +0 -182
  55. data/.travis.yml +0 -23
  56. data/assets/plastik/blue.png +0 -0
  57. data/assets/plastik/green.png +0 -0
  58. data/assets/plastik/red.png +0 -0
  59. data/lib/gruff/photo_bar.rb +0 -93
  60. data/lib/gruff/scene.rb +0 -198
  61. 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
- def draw
23
- super
22
+ private
24
23
 
25
- return unless data_given?
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 initialize_ivars
30
+ def initialize_attributes
31
31
  super
32
32
 
33
- @title_font_size = 20
33
+ @title_font.size = 20
34
+ @title_font.bold = false
34
35
  end
35
- private :initialize_ivars
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(@title_font_size, @title)
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
- [:high, :low].each_with_index do |indicator, index|
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: @font_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: @font_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(scale_fontsize(@title_font_size))
94
+ font_height = calculate_caps_height(@title_font)
94
95
 
95
- text_renderer = Gruff::Renderer::Text.new(@title, font: @font, size: @title_font_size, color: @font_color)
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 draw
18
- @has_left_labels = true
17
+ def initialize(*)
19
18
  super
19
+ @has_left_labels = true
20
+ end
20
21
 
21
- return unless data_given?
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.to_f
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.to_f / 2.0).round
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.to_f / 3.0).round, y_pos)
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
- 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)
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: @font, size: @marker_font_size, color: @font_color)
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, _bar_width = 0)
20
- left_x, left_y, right_x, _right_y = @coordinate
21
- if format.is_a?(Proc)
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 - 30 : left_y + 12
28
- yield left_x + (right_x - left_x) / 2, y, val
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, bar_width = 0)
35
- left_x, _left_y, right_x, right_y = @coordinate
36
- if format.is_a?(Proc)
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 + 40 : left_x - 40
43
- yield x, right_y - bar_width / 2, val
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, bar_width = 0, &block)
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, bar_width, &block)
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
- self.minimum_value = 0
21
+
22
+ raise "Can't handle negative values in stacked graph" if minimum_value < 0
22
23
  end
23
24
  end
@@ -28,19 +28,21 @@ class Gruff::Histogram < Gruff::Bar
28
28
  @data = []
29
29
  end
30
30
 
31
- def initialize_ivars
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 :initialize_ivars
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 draw
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|