gruff 0.15.0-java → 0.18.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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +21 -5
  3. data/.gitignore +1 -0
  4. data/.rubocop.yml +0 -12
  5. data/CHANGELOG.md +52 -0
  6. data/README.md +14 -3
  7. data/gruff.gemspec +3 -4
  8. data/lib/gruff/accumulator_bar.rb +1 -1
  9. data/lib/gruff/area.rb +6 -4
  10. data/lib/gruff/bar.rb +53 -32
  11. data/lib/gruff/base.rb +297 -186
  12. data/lib/gruff/bezier.rb +4 -2
  13. data/lib/gruff/box.rb +180 -0
  14. data/lib/gruff/bubble.rb +99 -0
  15. data/lib/gruff/bullet.rb +5 -5
  16. data/lib/gruff/candlestick.rb +120 -0
  17. data/lib/gruff/dot.rb +11 -12
  18. data/lib/gruff/font.rb +3 -0
  19. data/lib/gruff/helper/bar_conversion.rb +6 -10
  20. data/lib/gruff/helper/bar_mixin.rb +25 -0
  21. data/lib/gruff/helper/bar_value_label.rb +24 -43
  22. data/lib/gruff/helper/stacked_mixin.rb +19 -1
  23. data/lib/gruff/histogram.rb +9 -6
  24. data/lib/gruff/line.rb +67 -43
  25. data/lib/gruff/mini/legend.rb +15 -11
  26. data/lib/gruff/net.rb +23 -18
  27. data/lib/gruff/patch/string.rb +1 -0
  28. data/lib/gruff/pie.rb +26 -12
  29. data/lib/gruff/renderer/circle.rb +3 -1
  30. data/lib/gruff/renderer/dash_line.rb +3 -2
  31. data/lib/gruff/renderer/dot.rb +28 -15
  32. data/lib/gruff/renderer/line.rb +1 -3
  33. data/lib/gruff/renderer/rectangle.rb +6 -2
  34. data/lib/gruff/renderer/renderer.rb +0 -4
  35. data/lib/gruff/renderer/text.rb +7 -1
  36. data/lib/gruff/scatter.rb +84 -81
  37. data/lib/gruff/side_bar.rb +64 -31
  38. data/lib/gruff/side_stacked_bar.rb +43 -55
  39. data/lib/gruff/spider.rb +52 -14
  40. data/lib/gruff/stacked_area.rb +18 -8
  41. data/lib/gruff/stacked_bar.rb +59 -29
  42. data/lib/gruff/store/xy_data.rb +8 -9
  43. data/lib/gruff/store/xy_pointsizes_data.rb +60 -0
  44. data/lib/gruff/version.rb +1 -1
  45. data/lib/gruff.rb +11 -12
  46. metadata +9 -6
  47. data/lib/gruff/scene.rb +0 -208
  48. data/lib/gruff/store/custom_data.rb +0 -36
data/lib/gruff/bezier.rb CHANGED
@@ -22,15 +22,17 @@ class Gruff::Bezier < Gruff::Base
22
22
  private
23
23
 
24
24
  def draw_graph
25
- x_increment = @graph_width / (column_count - 1).to_f
25
+ x_increment = (@graph_width / (column_count - 1)).to_f
26
26
 
27
27
  store.norm_data.each do |data_row|
28
+ next if data_row[1].empty?
29
+
28
30
  poly_points = []
29
31
 
30
32
  data_row[1].each_with_index do |data_point, index|
31
33
  # Use incremented x and scaled y
32
34
  new_x = @graph_left + (x_increment * index)
33
- new_y = @graph_top + (@graph_height - data_point * @graph_height)
35
+ new_y = @graph_top + (@graph_height - (data_point * @graph_height))
34
36
 
35
37
  if index == 0 && RUBY_PLATFORM != 'java'
36
38
  poly_points << new_x
data/lib/gruff/box.rb ADDED
@@ -0,0 +1,180 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Here's how to set up a Gruff::Box.
5
+ #
6
+ # g = Gruff::Box.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::Box < 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) / column_count
48
+ bar_width = width * @spacing_factor
49
+ padding = width - bar_width
50
+
51
+ normalized_boxes.each_with_index do |box, index|
52
+ next if box.points.empty?
53
+
54
+ left_x = @graph_left + (width * index) + (padding / 2.0)
55
+ right_x = left_x + bar_width
56
+ center_x = (left_x + right_x) / 2.0
57
+
58
+ first_y, = conversion.get_top_bottom_scaled(box.first_quartile)
59
+ third_y, = conversion.get_top_bottom_scaled(box.third_quartile)
60
+ Gruff::Renderer::Rectangle.new(renderer, color: box.color, width: @stroke_width, opacity: @fill_opacity)
61
+ .render(left_x, first_y, right_x, third_y)
62
+
63
+ median_y, = conversion.get_top_bottom_scaled(box.median)
64
+ Gruff::Renderer::Line.new(renderer, color: box.color, width: @stroke_width * 2).render(left_x, median_y, right_x, median_y)
65
+
66
+ minmax_left_x = left_x + (bar_width / 4.0)
67
+ minmax_right_x = right_x - (bar_width / 4.0)
68
+ min_y, = conversion.get_top_bottom_scaled(box.lower_whisker)
69
+ Gruff::Renderer::Line.new(renderer, color: box.color, width: @stroke_width).render(minmax_left_x, min_y, minmax_right_x, min_y)
70
+ Gruff::Renderer::DashLine.new(renderer, color: box.color, width: @stroke_width, dasharray: [@stroke_width, @stroke_width * 2])
71
+ .render(center_x, min_y, center_x, first_y)
72
+
73
+ max_y, = conversion.get_top_bottom_scaled(box.upper_whisker)
74
+ Gruff::Renderer::Line.new(renderer, color: box.color, width: @stroke_width).render(minmax_left_x, max_y, minmax_right_x, max_y)
75
+ Gruff::Renderer::DashLine.new(renderer, color: box.color, width: @stroke_width, dasharray: [@stroke_width, @stroke_width * 2])
76
+ .render(center_x, max_y, center_x, third_y)
77
+
78
+ box.lower_outliers.each do |outlier|
79
+ outlier_y, = conversion.get_top_bottom_scaled(outlier)
80
+ Gruff::Renderer::Dot.new(renderer, :circle, color: box.color, opacity: @fill_opacity).render(center_x, outlier_y, @stroke_width * 2)
81
+ end
82
+
83
+ box.upper_outliers.each do |outlier|
84
+ outlier_y, = conversion.get_top_bottom_scaled(outlier)
85
+ Gruff::Renderer::Dot.new(renderer, :circle, color: box.color, opacity: @fill_opacity).render(center_x, outlier_y, @stroke_width * 2)
86
+ end
87
+
88
+ draw_label(center_x, index)
89
+ end
90
+ end
91
+
92
+ def normalized_boxes
93
+ @normalized_boxes ||= store.norm_data.map { |data| Gruff::Box::BoxData.new(data.label, data.points, data.color) }
94
+ end
95
+
96
+ def column_count
97
+ normalized_boxes.size
98
+ end
99
+
100
+ def calculate_spacing
101
+ column_count - 1
102
+ end
103
+
104
+ # @private
105
+ class BoxData < Struct.new(:label, :points, :color)
106
+ def initialize(label, points, color)
107
+ super(label, points.sort, color)
108
+ end
109
+
110
+ def min
111
+ points.first || 0
112
+ end
113
+
114
+ def max
115
+ points.last || 0
116
+ end
117
+
118
+ def min_whisker
119
+ [min, first_quartile - (1.5 * interquartile_range)].max
120
+ end
121
+
122
+ def max_whisker
123
+ [max, third_quartile + (1.5 * interquartile_range)].min
124
+ end
125
+
126
+ def upper_whisker
127
+ max = max_whisker
128
+ points.select { |point| point <= max }.max
129
+ end
130
+
131
+ def lower_whisker
132
+ min = min_whisker
133
+ points.select { |point| point >= min }.min
134
+ end
135
+
136
+ def median
137
+ if points.size.zero?
138
+ 0
139
+ elsif points.size.odd?
140
+ points[points.size / 2]
141
+ else
142
+ (points[points.size / 2] + points[(points.size / 2) - 1]) / 2.0
143
+ end
144
+ end
145
+
146
+ def first_quartile
147
+ if points.size.zero?
148
+ 0
149
+ elsif points.size.odd?
150
+ points[points.size / 4]
151
+ else
152
+ (points[points.size / 4] + points[(points.size / 4) - 1]) / 2.0
153
+ end
154
+ end
155
+
156
+ def third_quartile
157
+ if points.size.zero?
158
+ 0
159
+ elsif points.size.odd?
160
+ points[(points.size * 3) / 4]
161
+ else
162
+ (points[(points.size * 3) / 4] + points[((points.size * 3) / 4) - 1]) / 2.0
163
+ end
164
+ end
165
+
166
+ def lower_outliers
167
+ min = lower_whisker
168
+ points.select { |point| point < min }
169
+ end
170
+
171
+ def upper_outliers
172
+ max = upper_whisker
173
+ points.select { |point| point > max }
174
+ end
175
+
176
+ def interquartile_range
177
+ third_quartile - first_quartile
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Here's how to set up a Gruff::Bubble.
5
+ #
6
+ # g = Gruff::Bubble.new
7
+ # g.title = 'Bubble plot'
8
+ # g.write('bubble.png')
9
+ #
10
+ class Gruff::Bubble < Gruff::Scatter
11
+ # Specifies the filling opacity in area graph. Default is +0.6+.
12
+ attr_writer :fill_opacity
13
+
14
+ # Specifies the stroke width in line. Default is +1.0+.
15
+ attr_writer :stroke_width
16
+
17
+ # The first parameter is the name of the dataset. The next two are the
18
+ # x and y axis data points contain in their own array in that respective
19
+ # order. The 4th argument represents sizes of points.
20
+ # The final parameter is the color.
21
+ #
22
+ # Can be called multiple times with different datasets for a multi-valued
23
+ # graph.
24
+ #
25
+ # If the color argument is nil, the next color from the default theme will
26
+ # be used.
27
+ #
28
+ # @note If you want to use a preset theme, you must set it before calling {#data}.
29
+ #
30
+ # @param name [String, Symbol] containing the name of the dataset.
31
+ # @param x_data_points [Array] An Array of x-axis data points.
32
+ # @param y_data_points [Array] An Array of y-axis data points.
33
+ # @param point_sizes [Array] An Array of sizes for points.
34
+ # @param color [String] The hex string for the color of the dataset. Defaults to nil.
35
+ #
36
+ # @raise [ArgumentError] Data points contain nil values.
37
+ # This error will get raised if either the x or y axis data points array
38
+ # contains a +nil+ value. The graph will not make an assumption
39
+ # as how to graph +nil+.
40
+ # @raise [ArgumentError] +x_data_points+ is empty.
41
+ # This error is raised when the array for the x-axis points are empty
42
+ # @raise [ArgumentError] +y_data_points+ is empty.
43
+ # This error is raised when the array for the y-axis points are empty.
44
+ # @raise [ArgumentError] +point_sizes+ is empty.
45
+ # This error is raised when the array for the point_sizes are empty
46
+ # @raise [ArgumentError] +x_data_points.length != y_data_points.length+.
47
+ # Error means that the x and y axis point arrays do not match in length.
48
+ # @raise [ArgumentError] +x_data_points.length != point_sizes.length+.
49
+ # Error means that the x and point_sizes arrays do not match in length.
50
+ #
51
+ # @example
52
+ # g = Gruff::Bubble.new
53
+ # g.title = "Bubble Graph"
54
+ # g.data :A, [-1, 19, -4, -23], [-35, 21, 23, -4], [4.5, 1.0, 2.1, 0.9]
55
+ #
56
+ def data(name, x_data_points = [], y_data_points = [], point_sizes = [], color = nil)
57
+ # make sure it's an array
58
+ x_data_points = Array(x_data_points)
59
+ y_data_points = Array(y_data_points)
60
+ point_sizes = Array(point_sizes)
61
+
62
+ raise ArgumentError, 'Data Points contain nil Value!' if x_data_points.include?(nil) || y_data_points.include?(nil)
63
+ raise ArgumentError, 'x_data_points is empty!' if x_data_points.empty?
64
+ raise ArgumentError, 'y_data_points is empty!' if y_data_points.empty?
65
+ raise ArgumentError, 'point_sizes is empty!' if point_sizes.empty?
66
+ raise ArgumentError, 'x_data_points.length != y_data_points.length!' if x_data_points.length != y_data_points.length
67
+ raise ArgumentError, 'x_data_points.length != point_sizes.length!' if x_data_points.length != point_sizes.length
68
+
69
+ store.add(name, x_data_points, y_data_points, point_sizes, color)
70
+ end
71
+
72
+ private
73
+
74
+ def initialize_store
75
+ @store = Gruff::Store.new(Gruff::Store::XYPointsizeData)
76
+ end
77
+
78
+ def initialize_attributes
79
+ super
80
+
81
+ @fill_opacity = 0.6
82
+ @stroke_width = 1.0
83
+ end
84
+
85
+ def draw_graph
86
+ store.norm_data.each do |data_row|
87
+ data_row.coordinate_and_pointsizes.each do |x_value, y_value, point_size|
88
+ next if y_value.nil? || x_value.nil?
89
+
90
+ new_x = @graph_left + (x_value * @graph_width)
91
+ new_y = @graph_bottom - (y_value * @graph_height)
92
+ diameter = @graph_width / (@marker_count * x_increment) * point_size.to_f
93
+
94
+ Gruff::Renderer::Circle.new(renderer, color: data_row.color, width: @stroke_width, opacity: @fill_opacity)
95
+ .render(new_x, new_y, new_x - (diameter / 2.0), new_y)
96
+ end
97
+ end
98
+ end
99
+ end
data/lib/gruff/bullet.rb CHANGED
@@ -67,23 +67,23 @@ class Gruff::Bullet < Gruff::Base
67
67
  %i[high low].each_with_index do |indicator, index|
68
68
  next unless @options.key?(indicator)
69
69
 
70
- indicator_width_x = graph_left + graph_width * (@options[indicator] / maximum_value)
70
+ indicator_width_x = graph_left + (graph_width * (@options[indicator] / maximum_value))
71
71
 
72
72
  rect_renderer = Gruff::Renderer::Rectangle.new(renderer, color: @colors[index + 1])
73
73
  rect_renderer.render(graph_left, 0, indicator_width_x, graph_height)
74
74
  end
75
75
 
76
76
  if @options.key?(:target)
77
- target_x = graph_left + graph_width * (@options[:target] / maximum_value)
77
+ target_x = graph_left + (graph_width * (@options[:target] / maximum_value))
78
78
  half_thickness = thickness / 2.0
79
79
 
80
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
+ rect_renderer.render(target_x, half_thickness, target_x + half_thickness, (thickness * 2) + half_thickness)
82
82
  end
83
83
 
84
84
  # Value
85
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
+ rect_renderer.render(graph_left, thickness, graph_left + (graph_width * (@value / maximum_value)), thickness * 2)
87
87
  end
88
88
 
89
89
  private
@@ -94,6 +94,6 @@ private
94
94
  font_height = calculate_caps_height(@title_font)
95
95
 
96
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, font_height / 2, Magick::NorthWestGravity)
97
+ text_renderer.add_to_render_queue(1.0, 1.0, font_height / 2.0, font_height / 2.0, Magick::NorthWestGravity)
98
98
  end
99
99
  end
@@ -0,0 +1,120 @@
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
+ # The sort feature is not supported in this graph.
42
+ def sort=(_value)
43
+ raise 'Not support #sort= in Gruff::Candlestick'
44
+ end
45
+
46
+ # The sort feature is not supported in this graph.
47
+ def sorted_drawing=(_value)
48
+ raise 'Not support #sorted_drawing= in Gruff::Candlestick'
49
+ end
50
+
51
+ def data(low:, high:, open:, close:)
52
+ super('', [low, high, open, close])
53
+ end
54
+
55
+ private
56
+
57
+ def initialize_attributes
58
+ super
59
+ @show_vertical_markers = false
60
+ @fill_opacity = 0.4
61
+ @spacing_factor = 0.9
62
+ @stroke_width = 2.0
63
+ @up_color = '#579773'
64
+ @down_color = '#eb5242'
65
+
66
+ @hide_legend = true
67
+ end
68
+
69
+ def draw_graph
70
+ # Setup the BarConversion Object
71
+ conversion = Gruff::BarConversion.new(
72
+ top: @graph_top, bottom: @graph_bottom,
73
+ minimum_value: minimum_value, maximum_value: maximum_value, spread: @spread
74
+ )
75
+
76
+ width = (@graph_width - calculate_spacing) / column_count
77
+ bar_width = width * @spacing_factor
78
+ padding = width - bar_width
79
+
80
+ normalized_candlesticks.each_with_index do |candlestick, index|
81
+ left_x = @graph_left + (width * index) + (padding / 2.0)
82
+ right_x = left_x + bar_width
83
+ center_x = (left_x + right_x) / 2.0
84
+ color = candlestick.close >= candlestick.open ? @up_color : @down_color
85
+
86
+ draw_label(center_x, index) { draw_marker_vertical_line(center_x) if show_marker_vertical_line? }
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
+ @normalized_candlesticks ||= store.norm_data.map { |data| Gruff::Candlestick::CandlestickData.new(*data.points) }
103
+ end
104
+
105
+ def column_count
106
+ normalized_candlesticks.size
107
+ end
108
+
109
+ def calculate_spacing
110
+ column_count - 1
111
+ end
112
+
113
+ def show_marker_vertical_line?
114
+ !@hide_line_markers && @show_vertical_markers
115
+ end
116
+
117
+ # @private
118
+ class CandlestickData < Struct.new(:low, :high, :open, :close)
119
+ end
120
+ end
data/lib/gruff/dot.rb CHANGED
@@ -14,32 +14,33 @@
14
14
  # g.write('dot.png')
15
15
  #
16
16
  class Gruff::Dot < Gruff::Base
17
- private
18
-
19
- def initialize_attributes
17
+ def initialize(*)
20
18
  super
21
19
  @has_left_labels = true
20
+ @dot_style = 'circle'
22
21
  end
23
22
 
23
+ private
24
+
24
25
  def draw_graph
25
26
  # Setup spacing.
26
27
  #
27
28
  spacing_factor = 1.0
28
29
 
29
- items_width = @graph_height / column_count.to_f
30
+ items_width = @graph_height / column_count
30
31
  item_width = items_width * spacing_factor / store.length
31
32
  padding = (items_width * (1 - spacing_factor)) / 2
32
33
 
33
34
  store.norm_data.each_with_index do |data_row, row_index|
34
35
  data_row.points.each_with_index do |data_point, point_index|
35
36
  x_pos = @graph_left + (data_point * @graph_width)
36
- y_pos = @graph_top + (items_width * point_index) + padding + (items_width.to_f / 2.0).round
37
+ y_pos = @graph_top + (items_width * point_index) + padding + (items_width / 2.0)
37
38
 
38
39
  if row_index == 0
39
40
  Gruff::Renderer::Line.new(renderer, color: @marker_color).render(@graph_left, y_pos, @graph_left + @graph_width, y_pos)
40
41
  end
41
42
 
42
- Gruff::Renderer::Circle.new(renderer, color: data_row.color).render(x_pos, y_pos, x_pos + (item_width.to_f / 3.0).round, y_pos)
43
+ Gruff::Renderer::Dot.new(renderer, @dot_style, color: data_row.color).render(x_pos, y_pos, item_width / 3.0)
43
44
 
44
45
  draw_label(y_pos, point_index)
45
46
  end
@@ -51,11 +52,9 @@ private
51
52
  return if @hide_line_markers
52
53
 
53
54
  (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
56
-
57
- line_renderer = Gruff::Renderer::Line.new(renderer, color: @marker_color, shadow_color: @marker_shadow_color)
58
- line_renderer.render(x, @graph_bottom, x, @graph_bottom + 5)
55
+ marker_label = (BigDecimal(index.to_s) * BigDecimal(@increment.to_s)) + BigDecimal(minimum_value.to_s)
56
+ x = @graph_left + ((marker_label - minimum_value) * @graph_width / @spread)
57
+ draw_marker_vertical_line(x, tick_mark_mode: true)
59
58
 
60
59
  unless @hide_line_numbers
61
60
  label = y_axis_label(marker_label, @increment)
@@ -70,7 +69,7 @@ private
70
69
 
71
70
  def draw_label(y_offset, index)
72
71
  draw_unique_label(index) do
73
- draw_label_at(@graph_left - LABEL_MARGIN, 1.0, 0.0, y_offset, @labels[index], Magick::EastGravity)
72
+ draw_label_at(@graph_left - LABEL_MARGIN, 1.0, 0.0, y_offset, @labels[index], gravity: Magick::EastGravity)
74
73
  end
75
74
  end
76
75
  end
data/lib/gruff/font.rb CHANGED
@@ -3,7 +3,10 @@
3
3
  # Handle font setting to draw texts
4
4
  class Gruff::Font
5
5
  BOLD_PATH = File.expand_path(File.join(__FILE__, '../../../assets/fonts/Roboto-Bold.ttf')).freeze
6
+ private_constant :BOLD_PATH
7
+
6
8
  REGULAR_PATH = File.expand_path(File.join(__FILE__, '../../../assets/fonts/Roboto-Regular.ttf')).freeze
9
+ private_constant :REGULAR_PATH
7
10
 
8
11
  # Get/set font path.
9
12
  attr_accessor :path
@@ -13,8 +13,6 @@
13
13
  #
14
14
  # @private
15
15
  class Gruff::BarConversion
16
- attr_writer :mode
17
-
18
16
  def initialize(top:, bottom:, minimum_value:, maximum_value:, spread:)
19
17
  @graph_top = top
20
18
  @graph_height = bottom - top
@@ -36,25 +34,23 @@ class Gruff::BarConversion
36
34
  end
37
35
 
38
36
  def get_top_bottom_scaled(data_point)
37
+ data_point = data_point.to_f
39
38
  result = []
40
39
 
41
40
  case @mode
42
41
  when 1
43
42
  # minimum value >= 0 ( only positive values )
44
- result[0] = @graph_top + @graph_height * (1 - data_point)
43
+ result[0] = @graph_top + (@graph_height * (1 - data_point))
45
44
  result[1] = @graph_top + @graph_height
46
45
  when 2
47
46
  # only negative values
48
47
  result[0] = @graph_top
49
- result[1] = @graph_top + @graph_height * (1 - data_point)
48
+ result[1] = @graph_top + (@graph_height * (1 - data_point))
50
49
  when 3
51
50
  # 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)
55
- else
56
- result[0] = 0.0
57
- result[1] = 0.0
51
+ val = data_point - (@minimum_value / @spread)
52
+ result[0] = @graph_top + (@graph_height * (1 - (val - @zero)))
53
+ result[1] = @graph_top + (@graph_height * (1 - @zero))
58
54
  end
59
55
 
60
56
  result
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @private
4
+ module Gruff::Base::BarMixin
5
+ def normalized_group_bars
6
+ @normalized_group_bars ||= begin
7
+ group_bars = Array.new(column_count) { [] }
8
+ store.norm_data.each_with_index do |data_row, row_index|
9
+ data_row.points.each_with_index do |data_point, point_index|
10
+ group_bars[point_index] << BarData.new(data_point, store.data[row_index].points[point_index], data_row.color)
11
+ end
12
+
13
+ # Adjust the number of each group with empty bar
14
+ (data_row.points.size..(column_count - 1)).each do |index|
15
+ group_bars[index] << BarData.new(0, nil, data_row.color)
16
+ end
17
+ end
18
+ group_bars
19
+ end
20
+ end
21
+
22
+ # @private
23
+ class BarData < Struct.new(:point, :value, :color)
24
+ end
25
+ end
@@ -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,56 +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
- val = begin
22
- if format.is_a?(Proc)
23
- format.call(@value)
24
- else
25
- sprintf(format || '%.2f', @value).commify
26
- end
27
- 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)
28
36
 
29
- y = @value >= 0 ? left_y - 30 : left_y + 12
30
- 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
31
39
  end
32
40
  end
33
41
 
34
42
  # @private
35
43
  class SideBar < Base
36
- def prepare_rendering(format, bar_width = 0)
37
- left_x, _left_y, right_x, right_y = @coordinate
38
- val = begin
39
- if format.is_a?(Proc)
40
- format.call(@value)
41
- else
42
- sprintf(format || '%.2f', @value).commify
43
- end
44
- end
45
- x = @value >= 0 ? right_x + 40 : left_x - 40
46
- yield x, right_y - bar_width / 2, val
47
- end
48
- end
49
-
50
- # @private
51
- class StackedBar
52
- def initialize
53
- @bars = []
54
- end
55
-
56
- def add(bar, index)
57
- bars = @bars[index] || []
58
- bars << bar
59
- @bars[index] = bars
60
- 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)
61
47
 
62
- def prepare_rendering(format, bar_width = 0, &block)
63
- @bars.each do |bars|
64
- value = bars.sum(&:value)
65
- bar = bars.last
66
- bar_value_label = bar.class.new(bar.coordinate, value)
67
- bar_value_label.prepare_rendering(format, bar_width, &block)
68
- end
48
+ x = @value >= 0 ? right_x + 10 : left_x - metrics.width - 10
49
+ yield x, left_y, val, metrics.width, metrics.height
69
50
  end
70
51
  end
71
52
  end