gruff 0.17.0 → 0.18.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f16153da348397448dc35f4d6435860b53040b14b0a7cbc3ca84bcaf9dd68b90
4
- data.tar.gz: 68efd965f5b00403281ff46cc377fae9503b3b4290f49b66e6ceb424d24b4d27
3
+ metadata.gz: 1b7a28fa80f8c598a927ce29242c2014a38cc21b1fc1fc43a80285239b448176
4
+ data.tar.gz: 13a94cca6581eebccafd48de9d48daa1acd4c085989cd996a6054fd63bfa8f8a
5
5
  SHA512:
6
- metadata.gz: c2436acee944766653a6497854eb89493fc67f940d846e7299bdf92e2a31c8f7e267f508dad3b8593d596d31a277702983a58b7a2282b00395c33c152c657407
7
- data.tar.gz: 7d75accebb94e9ae9362ae487265de84b7bbd6b6b90087c3ce5bd0f0e573d51d270f74bd04f3451d64aedba189fc09d5ef69b3a8c14785cb1b7d2c24b8581cc5
6
+ metadata.gz: 165988fc75b87fcd15f789ee9b178cbbe4ddc32cd0090f62cdcd36041806223b65c59406cd855038e0881ffb0b260df146aec74de7caff5ee0fc32df01447268
7
+ data.tar.gz: c5dcff59b2f8ab733fc99bafee1472ab0c014fbb106bec26fca167880b585cc9e61a6cc00b4e08aa02d39a2fe92d2291a7363a13ed16673a06898edcd15fb081
@@ -37,7 +37,7 @@ jobs:
37
37
  matrix:
38
38
  ruby-version: ['2.5', '2.6', '2.7', '3.0', '3.1']
39
39
  imagemagick-version:
40
- - { full: 7.1.0-35, major-minor: '7.0' }
40
+ - { full: 7.1.0-39, major-minor: '7.0' }
41
41
  name: Ruby ${{ matrix.ruby-version }}
42
42
  steps:
43
43
  - uses: actions/checkout@v3
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Change Log
2
2
 
3
+ ## 0.18.0
4
+ - Add Gruff::Bubble (#604)
5
+ - Rename Gruff::BoxPlot to Gruff::Box (#603)
6
+ - Mark Gruff::Scatter#disable_significant_rounding_x_axis= as deprecated (#602)
7
+ - Mark Gruff::Scatter#x_label_margin= as deprecated (#601)
8
+ - Mark Gruff::Scatter#use_vertical_x_labels= as deprecated (#600)
9
+
3
10
  ## 0.17.0
4
11
  - Add Gruff::Base#label_rotation= method to rotate bottom label (#599)
5
12
  - Mark #label_stagger_height= as deprecated (#598)
data/README.md CHANGED
@@ -126,14 +126,18 @@ In progress!
126
126
 
127
127
  ![Histogram chart](https://raw.github.com/topfunky/gruff/master/test/expected/histogram.png)
128
128
 
129
- ### Box plot
129
+ ### Box chart
130
130
 
131
- ![Box plot](https://raw.github.com/topfunky/gruff/master/test/expected/box_plot.png)
131
+ ![Box chart](https://raw.github.com/topfunky/gruff/master/test/expected/box.png)
132
132
 
133
133
  ### Candlestick
134
134
 
135
135
  ![Candlestick](https://raw.github.com/topfunky/gruff/master/test/expected/candlestick.png)
136
136
 
137
+ ### Bubble chart
138
+
139
+ ![Bubble chart](https://raw.github.com/topfunky/gruff/master/test/expected/bubble.png)
140
+
137
141
  ## Documentation
138
142
 
139
143
  http://www.rubydoc.info/github/topfunky/gruff/frames
data/lib/gruff/area.rb CHANGED
@@ -30,7 +30,7 @@ private
30
30
  end
31
31
 
32
32
  def draw_graph
33
- x_increment = @graph_width / (column_count - 1)
33
+ x_increment = (@graph_width / (column_count - 1)).to_f
34
34
 
35
35
  store.norm_data.each do |data_row|
36
36
  next if data_row.points.empty?
data/lib/gruff/bar.rb CHANGED
@@ -114,7 +114,6 @@ private
114
114
  minimum_value: minimum_value, maximum_value: maximum_value, spread: @spread
115
115
  )
116
116
 
117
- group_spacing = @group_spacing * @scale
118
117
  group_left_x = @graph_left
119
118
 
120
119
  normalized_group_bars.each_with_index do |group_bars, group_index|
@@ -140,7 +139,7 @@ private
140
139
  label_center = group_left_x + ((right_x - group_left_x) / 2.0)
141
140
  draw_label(label_center, group_index)
142
141
 
143
- group_left_x = right_x + padding + group_spacing
142
+ group_left_x = right_x + padding + @group_spacing
144
143
  end
145
144
 
146
145
  # Draw the last label if requested
@@ -148,7 +147,7 @@ private
148
147
  end
149
148
 
150
149
  def calculate_spacing
151
- @scale * @group_spacing * (column_count - 1)
150
+ @group_spacing * (column_count - 1)
152
151
  end
153
152
 
154
153
  def proc_text_metrics
data/lib/gruff/base.rb CHANGED
@@ -607,8 +607,8 @@ module Gruff
607
607
  end
608
608
 
609
609
  def calculate_spread
610
- @spread = maximum_value - minimum_value
611
- @spread = @spread > 0 ? @spread : 1
610
+ @spread = maximum_value.to_f - minimum_value.to_f
611
+ @spread = @spread > 0 ? @spread : 1.0
612
612
  end
613
613
 
614
614
  def hide_title?
@@ -663,7 +663,7 @@ module Gruff
663
663
  def draw_line_markers
664
664
  return if @hide_line_markers
665
665
 
666
- increment_scaled = @graph_height / (@spread / @increment)
666
+ increment_scaled = (@graph_height / (@spread / @increment)).to_f
667
667
 
668
668
  # Draw horizontal line markers and annotate with numbers
669
669
  (0..marker_count).each do |index|
@@ -766,25 +766,14 @@ module Gruff
766
766
  text_renderer.add_to_render_queue(@raw_columns, 1.0, 0, @top_margin)
767
767
  end
768
768
 
769
- # Draws column labels below graph, centered over x_offset
770
- def draw_label(x_offset, index, gravity = Magick::NorthGravity, &block)
769
+ # Draws column labels below graph, centered over x
770
+ def draw_label(x, index, gravity = Magick::NorthGravity, &block)
771
771
  draw_unique_label(index) do
772
- y_offset = @graph_bottom
773
-
774
- if x_offset >= @graph_left && x_offset <= @graph_right
775
- width = calculate_width(@marker_font, @labels[index], rotation: @label_rotation)
776
- height = calculate_height(@marker_font, @labels[index], rotation: @label_rotation)
777
- case @label_rotation
778
- when 0
779
- x_offset
780
- when 0..45
781
- x_offset += (width / 2.0)
782
- when -45..0
783
- x_offset -= (width / 2.0)
784
- end
785
- y_offset += (height / 2.0) > LABEL_MARGIN ? (height / 2.0) : LABEL_MARGIN
772
+ if x >= @graph_left && x <= @graph_right
773
+ y = @graph_bottom
774
+ x_offset, y_offset = calculate_label_offset(@marker_font, @labels[index], LABEL_MARGIN, @label_rotation)
786
775
 
787
- draw_label_at(1.0, 1.0, x_offset, y_offset, @labels[index], gravity: gravity, rotation: @label_rotation)
776
+ draw_label_at(1.0, 1.0, x + x_offset, y + y_offset, @labels[index], gravity: gravity, rotation: @label_rotation)
788
777
  yield if block
789
778
  end
790
779
  end
@@ -831,10 +820,6 @@ module Gruff
831
820
  @theme_options = {}
832
821
  end
833
822
 
834
- def scale(value)
835
- value * @scale
836
- end
837
-
838
823
  def clip_value_if_greater_than(value, max_value)
839
824
  value > max_value ? max_value : value
840
825
  end
@@ -1123,14 +1108,32 @@ module Gruff
1123
1108
  # Try to use a number of horizontal lines that will come out even.
1124
1109
  #
1125
1110
  # TODO Do the same for larger numbers...100, 75, 50, 25
1126
- @increment = @spread > 0 && marker_count > 0 ? significant(@spread / marker_count) : 1
1111
+ @increment = @spread > 0 && marker_count > 0 ? significant(@spread / marker_count) : 1.0
1127
1112
  else
1128
1113
  # TODO: Make this work for negative values
1129
1114
  self.marker_count = (@spread / @y_axis_increment).to_i
1130
- @increment = @y_axis_increment
1115
+ @increment = @y_axis_increment.to_f
1131
1116
  end
1132
1117
  end
1133
1118
 
1119
+ def calculate_label_offset(font, label, margin, rotation)
1120
+ width = calculate_width(font, label, rotation: rotation)
1121
+ height = calculate_height(font, label, rotation: rotation)
1122
+ x_offset = begin
1123
+ case rotation
1124
+ when 0
1125
+ 0
1126
+ when 0..45
1127
+ width / 2.0
1128
+ when -45..0
1129
+ -(width / 2.0)
1130
+ end
1131
+ end
1132
+ y_offset = (height / 2.0) > margin ? (height / 2.0) : margin
1133
+
1134
+ [x_offset, y_offset]
1135
+ end
1136
+
1134
1137
  # Used for degree <=> radian conversions
1135
1138
  def deg2rad(angle)
1136
1139
  (angle * Math::PI) / 180.0
data/lib/gruff/bezier.rb CHANGED
@@ -22,7 +22,7 @@ class Gruff::Bezier < Gruff::Base
22
22
  private
23
23
 
24
24
  def draw_graph
25
- x_increment = @graph_width / (column_count - 1)
25
+ x_increment = (@graph_width / (column_count - 1)).to_f
26
26
 
27
27
  store.norm_data.each do |data_row|
28
28
  next if data_row[1].empty?
@@ -1,15 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #
4
- # Here's how to set up a Gruff::BoxPlot.
4
+ # Here's how to set up a Gruff::Box.
5
5
  #
6
- # g = Gruff::BoxPlot.new
6
+ # g = Gruff::Box.new
7
7
  # g.data "A", [2, 3, 5, 6, 8, 10, 11, 15, 17, 20, 28, 29, 33, 34, 45, 46, 49, 61]
8
8
  # g.data "B", [3, 4, 34, 35, 38, 39, 45, 60, 61, 69, 80, 130]
9
9
  # g.data "C", [4, 40, 41, 46, 57, 64, 77, 76, 79, 78, 99, 153]
10
10
  # g.write("box_plot.png")
11
11
  #
12
- class Gruff::BoxPlot < Gruff::Base
12
+ class Gruff::Box < Gruff::Base
13
13
  # Specifies the filling opacity in area graph. Default is +0.2+.
14
14
  attr_writer :fill_opacity
15
15
 
@@ -90,7 +90,7 @@ private
90
90
  end
91
91
 
92
92
  def normalized_boxes
93
- @normalized_boxes ||= store.norm_data.map { |data| Gruff::BoxPlot::BoxData.new(data.label, data.points, data.color) }
93
+ @normalized_boxes ||= store.norm_data.map { |data| Gruff::Box::BoxData.new(data.label, data.points, data.color) }
94
94
  end
95
95
 
96
96
  def column_count
@@ -98,7 +98,7 @@ private
98
98
  end
99
99
 
100
100
  def calculate_spacing
101
- @scale * (column_count - 1)
101
+ column_count - 1
102
102
  end
103
103
 
104
104
  # @private
@@ -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
@@ -107,7 +107,7 @@ private
107
107
  end
108
108
 
109
109
  def calculate_spacing
110
- @scale * (column_count - 1)
110
+ column_count - 1
111
111
  end
112
112
 
113
113
  def show_marker_vertical_line?
@@ -40,7 +40,6 @@ private
40
40
  @minimum_bin = nil
41
41
  @maximum_bin = nil
42
42
  end
43
- private :initialize_attributes
44
43
 
45
44
  def setup_data
46
45
  @data.each do |(name, data_points, color)|
data/lib/gruff/line.rb CHANGED
@@ -81,6 +81,30 @@ class Gruff::Line < Gruff::Base
81
81
  @reference_lines[:baseline][:color] = new_value
82
82
  end
83
83
 
84
+ # Input the data in the graph.
85
+ #
86
+ # Parameters are an array where the first element is the name of the dataset
87
+ # and the value is an array of values to plot.
88
+ #
89
+ # Can be called multiple times with different datasets for a multi-valued
90
+ # graph.
91
+ #
92
+ # If the color argument is nil, the next color from the default theme will
93
+ # be used.
94
+ #
95
+ # @param name [String, Symbol] The name of the dataset.
96
+ # @param data_points [Array] The array of dataset.
97
+ # @param color [String] The color for drawing graph of dataset.
98
+ #
99
+ # @note
100
+ # If you want to use a preset theme, you must set it before calling {#data}.
101
+ #
102
+ # @example
103
+ # data("Bart S.", [95, 45, 78, 89, 88, 76], '#ffcc00')
104
+ def data(name, data_points = [], color = nil)
105
+ store.add(name, nil, data_points, color)
106
+ end
107
+
84
108
  # This method allows one to plot a dataset with both X and Y data.
85
109
  #
86
110
  # @overload dataxy(name, x_data_points = [], y_data_points = [], color = nil)
@@ -131,7 +155,7 @@ class Gruff::Line < Gruff::Base
131
155
  raise ArgumentError, 'x_data_points.length != y_data_points.length!' if x_data_points.length != y_data_points.length
132
156
 
133
157
  # call the existing data routine for the x/y data.
134
- store.add(name, y_data_points, color, x_data_points)
158
+ store.add(name, x_data_points, y_data_points, color)
135
159
  end
136
160
 
137
161
  private
@@ -177,6 +201,7 @@ private
177
201
  def draw_graph
178
202
  # Check to see if more than one datapoint was given. NaN can result otherwise.
179
203
  @x_increment = column_count > 1 ? @graph_width / (column_count - 1) : @graph_width
204
+ @x_increment = @x_increment.to_f
180
205
 
181
206
  @reference_lines.each_value do |curr_reference_line|
182
207
  draw_horizontal_reference_line(curr_reference_line) if curr_reference_line.key?(:norm_value)
@@ -194,7 +219,7 @@ private
194
219
  # use the old method: equally spaced points along the x-axis
195
220
  @graph_left + (@x_increment * index)
196
221
  else
197
- get_x_coord(x_data, @graph_width, @graph_left)
222
+ @graph_left + (x_data * @graph_width)
198
223
  end
199
224
  end
200
225
  draw_label_for_x_data(x_data, new_x, index)
@@ -287,10 +312,6 @@ private
287
312
  end
288
313
  end
289
314
 
290
- def get_x_coord(x_data_point, width, offset)
291
- (x_data_point * width) + offset
292
- end
293
-
294
315
  def contains_one_point_only?(data_row)
295
316
  data_row.y_points.compact.count == 1
296
317
  end
@@ -101,6 +101,10 @@ module Gruff
101
101
  end
102
102
  truncated_label + (truncated_label.length < label.to_s.length ? '...' : '')
103
103
  end
104
+
105
+ def scale(value)
106
+ value * @scale
107
+ end
104
108
  end
105
109
  end
106
110
  end
data/lib/gruff/pie.rb CHANGED
@@ -38,7 +38,7 @@ class Gruff::Pie < Gruff::Base
38
38
 
39
39
  # Can be used to make the pie start cutting slices at the top (-90.0)
40
40
  # or at another angle. Default is +-90.0+, which starts at 3 o'clock.
41
- # @deprecated Please use +start_degree+ attribute instead.
41
+ # @deprecated Please use {#start_degree=} instead.
42
42
  def zero_degree=(value)
43
43
  warn '#zero_degree= is deprecated. Please use `start_degree` attribute instead'
44
44
  @start_degree = value
@@ -3,15 +3,17 @@
3
3
  module Gruff
4
4
  # @private
5
5
  class Renderer::Circle
6
- def initialize(renderer, color:, width: 1.0)
6
+ def initialize(renderer, color:, width: 1.0, opacity: 1.0)
7
7
  @renderer = renderer
8
8
  @color = color
9
9
  @width = width
10
+ @opacity = opacity
10
11
  end
11
12
 
12
13
  def render(origin_x, origin_y, perim_x, perim_y)
13
14
  @renderer.draw.push
14
15
  @renderer.draw.fill(@color)
16
+ @renderer.draw.fill_opacity(@opacity)
15
17
  @renderer.draw.stroke(@color)
16
18
  @renderer.draw.stroke_width(@width)
17
19
  @renderer.draw.circle(origin_x, origin_y, perim_x, perim_y)
data/lib/gruff/scatter.rb CHANGED
@@ -24,24 +24,35 @@ class Gruff::Scatter < Gruff::Base
24
24
  attr_writer :circle_radius
25
25
  attr_writer :stroke_width
26
26
 
27
- # Allow disabling the significant rounding when labeling the X axis.
28
- # This is useful when working with a small range of high values (for example, a date range of months, while seconds as units).
29
- attr_writer :disable_significant_rounding_x_axis
30
-
31
27
  # Allow for vertical marker lines.
32
28
  attr_writer :show_vertical_markers
33
29
 
34
- # Allow using vertical labels in the X axis (and setting the label margin).
35
- attr_writer :x_label_margin
36
- attr_writer :use_vertical_x_labels
37
-
38
30
  # Allow enabling vertical lines. When you have a lot of data, they can work great.
39
- # @deprecated Please use +show_vertical_markers+ attribute instead.
31
+ # @deprecated Please use {#show_vertical_markers=} instead.
40
32
  def enable_vertical_line_markers=(value)
41
33
  warn '#enable_vertical_line_markers= is deprecated. Please use `show_vertical_markers` attribute instead'
42
34
  @show_vertical_markers = value
43
35
  end
44
36
 
37
+ # Allow using vertical labels in the X axis.
38
+ # @deprecated Please use {Gruff::Base#label_rotation=} instead.
39
+ def use_vertical_x_labels=(_value)
40
+ warn '#use_vertical_x_labels= is deprecated. It is no longer effective. Please use `#label_rotation=` instead'
41
+ end
42
+
43
+ # Allow using vertical labels in the X axis (and setting the label margin).
44
+ # @deprecated
45
+ def x_label_margin=(_value)
46
+ warn '#x_label_margin= is deprecated. It is no longer effective.'
47
+ end
48
+
49
+ # Allow disabling the significant rounding when labeling the X axis.
50
+ # This is useful when working with a small range of high values (for example, a date range of months, while seconds as units).
51
+ # @deprecated
52
+ def disable_significant_rounding_x_axis=(_value)
53
+ warn '#disable_significant_rounding_x_axis= is deprecated. It is no longer effective.'
54
+ end
55
+
45
56
  # The first parameter is the name of the dataset. The next two are the
46
57
  # x and y axis data points contain in their own array in that respective
47
58
  # order. The final parameter is the color.
@@ -55,11 +66,10 @@ class Gruff::Scatter < Gruff::Base
55
66
  # @note If you want to use a preset theme, you must set it before calling {#data}.
56
67
  #
57
68
  # @param name [String, Symbol] containing the name of the dataset.
58
- # @param x_data_points [Array] An Array of of x-axis data points.
59
- # @param y_data_points [Array] An Array of of y-axis data points.
69
+ # @param x_data_points [Array] An Array of x-axis data points.
70
+ # @param y_data_points [Array] An Array of y-axis data points.
60
71
  # @param color [String] The hex string for the color of the dataset. Defaults to nil.
61
72
  #
62
- #
63
73
  # @raise [ArgumentError] Data points contain nil values.
64
74
  # This error will get raised if either the x or y axis data points array
65
75
  # contains a +nil+ value. The graph will not make an assumption
@@ -88,7 +98,7 @@ class Gruff::Scatter < Gruff::Base
88
98
  raise ArgumentError, 'x_data_points.length != y_data_points.length!' if x_data_points.length != y_data_points.length
89
99
 
90
100
  # Call the existing data routine for the x/y axis data
91
- store.add(name, y_data_points, color, x_data_points)
101
+ store.add(name, x_data_points, y_data_points, color)
92
102
  end
93
103
 
94
104
  alias dataxy data
@@ -102,16 +112,11 @@ private
102
112
  def initialize_attributes
103
113
  super
104
114
 
105
- @baseline_x_color = @baseline_y_color = 'red'
106
- @baseline_x_value = @baseline_y_value = nil
107
115
  @circle_radius = nil
108
- @disable_significant_rounding_x_axis = false
109
116
  @show_vertical_markers = false
110
117
  @marker_x_count = nil
111
118
  @maximum_x_value = @minimum_x_value = nil
112
119
  @stroke_width = nil
113
- @use_vertical_x_labels = false
114
- @x_label_margin = nil
115
120
  end
116
121
 
117
122
  def setup_drawing
@@ -141,11 +146,11 @@ private
141
146
  data_row.coordinates.each do |x_value, y_value|
142
147
  next if y_value.nil? || x_value.nil?
143
148
 
144
- new_x = get_x_coord(x_value, @graph_width, @graph_left)
145
- new_y = @graph_top + (@graph_height - (y_value * @graph_height))
149
+ new_x = @graph_left + (x_value * @graph_width)
150
+ new_y = @graph_bottom - (y_value * @graph_height)
146
151
 
147
152
  # Reset each time to avoid thin-line errors
148
- stroke_width = @stroke_width || clip_value_if_greater_than(@columns / (store.norm_data.first[1].size * 4), 5.0)
153
+ stroke_width = @stroke_width || clip_value_if_greater_than(@columns / (store.norm_data.first[1].size * 4.0), 5.0)
149
154
  circle_radius = @circle_radius || clip_value_if_greater_than(@columns / (store.norm_data.first[1].size * 2.5), 5.0)
150
155
  Gruff::Renderer::Circle.new(renderer, color: data_row.color, width: stroke_width).render(new_x, new_y, new_x - circle_radius, new_y)
151
156
  end
@@ -155,7 +160,7 @@ private
155
160
  def calculate_spread
156
161
  super
157
162
  @x_spread = @maximum_x_value.to_f - @minimum_x_value.to_f
158
- @x_spread = @x_spread > 0 ? @x_spread : 1
163
+ @x_spread = @x_spread > 0 ? @x_spread : 1.0
159
164
  end
160
165
 
161
166
  def normalize
@@ -169,32 +174,27 @@ private
169
174
  super
170
175
  return if @hide_line_markers
171
176
 
172
- increment_x_scaled = @graph_width / (@x_spread / x_increment)
177
+ increment_x_scaled = (@graph_width / (@x_spread / x_increment)).to_f
173
178
 
174
179
  # Draw vertical line markers and annotate with numbers
175
180
  (0..marker_x_count).each do |index|
176
181
  # TODO: Fix the vertical lines, and enable them by default. Not pretty when they don't match up with top y-axis line
177
182
  if @show_vertical_markers
178
- draw_marker_vertical_line(@graph_left + @graph_width - (index * increment_x_scaled))
183
+ draw_marker_vertical_line(@graph_left + (index * increment_x_scaled))
179
184
  end
180
185
 
181
186
  unless @hide_line_numbers
182
187
  marker_label = (BigDecimal(index.to_s) * BigDecimal(x_increment.to_s)) + BigDecimal(@minimum_x_value.to_s)
183
- y_offset = @graph_bottom + (@x_label_margin || LABEL_MARGIN)
184
- x_offset = get_x_coord(index, increment_x_scaled, @graph_left)
185
-
186
188
  label = x_axis_label(marker_label, x_increment)
187
- rotation = -90.0 if @use_vertical_x_labels
188
- text_renderer = Gruff::Renderer::Text.new(renderer, label, font: @marker_font, rotation: rotation)
189
- text_renderer.add_to_render_queue(1.0, 1.0, x_offset, y_offset)
189
+ x = @graph_left + (increment_x_scaled * index)
190
+ y = @graph_bottom
191
+ x_offset, y_offset = calculate_label_offset(@marker_font, label, LABEL_MARGIN, @label_rotation)
192
+
193
+ draw_label_at(1.0, 1.0, x + x_offset, y + y_offset, label, rotation: @label_rotation)
190
194
  end
191
195
  end
192
196
  end
193
197
 
194
- def get_x_coord(x_data_point, width, offset)
195
- (x_data_point * width) + offset
196
- end
197
-
198
198
  def marker_x_count
199
199
  # TODO: Do the same for larger numbers...100, 75, 50, 25
200
200
  @marker_x_count ||= begin
@@ -215,15 +215,10 @@ private
215
215
  def x_increment
216
216
  @x_increment ||= begin
217
217
  if @x_axis_increment.nil?
218
- increment = @x_spread > 0 ? (@x_spread / marker_x_count) : 1
219
- unless @disable_significant_rounding_x_axis
220
- increment = significant(increment)
221
- end
218
+ @x_spread > 0 ? significant(@x_spread / marker_x_count) : 1.0
222
219
  else
223
- increment = @x_axis_increment
220
+ @x_axis_increment.to_f
224
221
  end
225
-
226
- increment
227
222
  end
228
223
  end
229
224
  end
@@ -111,7 +111,6 @@ private
111
111
  minimum_value: minimum_value, maximum_value: maximum_value, spread: @spread
112
112
  )
113
113
 
114
- group_spacing = @group_spacing * @scale
115
114
  group_left_y = @graph_top
116
115
 
117
116
  normalized_group_bars.each_with_index do |group_bars, group_index|
@@ -137,7 +136,7 @@ private
137
136
  label_center = group_left_y + (bars_width / 2.0)
138
137
  draw_label(label_center, group_index)
139
138
 
140
- group_left_y = right_y + padding + group_spacing
139
+ group_left_y = right_y + padding + @group_spacing
141
140
  end
142
141
  end
143
142
 
@@ -176,7 +175,7 @@ private
176
175
  end
177
176
 
178
177
  def calculate_spacing
179
- @scale * @group_spacing * (column_count - 1)
178
+ @group_spacing * (column_count - 1)
180
179
  end
181
180
 
182
181
  def proc_text_metrics
@@ -33,7 +33,7 @@ private
33
33
  end
34
34
 
35
35
  def draw_graph
36
- x_increment = @graph_width / (column_count - 1)
36
+ x_increment = (@graph_width / (column_count - 1)).to_f
37
37
 
38
38
  height = Array.new(column_count, 0)
39
39
 
@@ -3,18 +3,17 @@
3
3
  module Gruff
4
4
  class Store
5
5
  # @private
6
- class XYData < Struct.new(:label, :y_points, :color, :x_points)
7
- def initialize(label, y_points, color, x_points = nil)
8
- x_points = Array(x_points) if x_points
9
- super(label.to_s, Array(y_points), color, x_points)
6
+ class XYData < Struct.new(:label, :x_points, :y_points, :color)
7
+ def initialize(label, x_points, y_points, color)
8
+ y_points = Array(y_points)
9
+ x_points = x_points ? Array(x_points) : Array.new(y_points.length)
10
+ raise ArgumentError, 'x_points.length != y_points.length!' if x_points.length != y_points.length
11
+
12
+ super(label.to_s, x_points, y_points, color)
10
13
  end
11
14
 
12
15
  alias points y_points
13
16
 
14
- def x_points
15
- self[:x_points] || Array.new(y_points.length)
16
- end
17
-
18
17
  def coordinates
19
18
  x_points.zip(y_points)
20
19
  end
@@ -53,7 +52,7 @@ module Gruff
53
52
  y.nil? ? nil : (y.to_f - minimum_y.to_f) / spread_y
54
53
  end
55
54
 
56
- self.class.new(label, norm_y_points, color, norm_x_points)
55
+ self.class.new(label, norm_x_points, norm_y_points, color)
57
56
  end
58
57
  end
59
58
  end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gruff
4
+ class Store
5
+ # @private
6
+ class XYPointsizeData < Struct.new(:label, :x_points, :y_points, :point_sizes, :color)
7
+ def initialize(label, x_points, y_points, point_sizes, color)
8
+ y_points = Array(y_points)
9
+ x_points = x_points ? Array(x_points) : Array.new(y_points.length)
10
+ raise ArgumentError, 'x_points.length != y_points.length!' if x_points.length != y_points.length
11
+ raise ArgumentError, 'x_points.length != point_sizes.length!' if x_points.length != point_sizes.length
12
+
13
+ super(label.to_s, x_points, y_points, point_sizes, color)
14
+ end
15
+
16
+ alias points y_points
17
+
18
+ def coordinate_and_pointsizes
19
+ x_points.zip(y_points, point_sizes)
20
+ end
21
+
22
+ def empty?
23
+ y_points.empty?
24
+ end
25
+
26
+ def columns
27
+ y_points.length
28
+ end
29
+
30
+ def min
31
+ y_points.compact.min
32
+ end
33
+ alias min_y min
34
+
35
+ def max
36
+ y_points.compact.max
37
+ end
38
+ alias max_y max
39
+
40
+ def min_x
41
+ x_points.compact.min
42
+ end
43
+
44
+ def max_x
45
+ x_points.compact.max
46
+ end
47
+
48
+ def normalize(minimum_x:, minimum_y:, spread_x:, spread_y:)
49
+ norm_x_points = x_points.map do |x|
50
+ x.nil? ? nil : (x.to_f - minimum_x.to_f) / spread_x
51
+ end
52
+ norm_y_points = y_points.map do |y|
53
+ y.nil? ? nil : (y.to_f - minimum_y.to_f) / spread_y
54
+ end
55
+
56
+ self.class.new(label, norm_x_points, norm_y_points, point_sizes, color)
57
+ end
58
+ end
59
+ end
60
+ end
data/lib/gruff/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Gruff
4
- VERSION = '0.17.0'
4
+ VERSION = '0.18.0'
5
5
  end
data/lib/gruff.rb CHANGED
@@ -26,7 +26,8 @@ module Gruff
26
26
  autoload :Area, Gruff.libpath('area')
27
27
  autoload :Bar, Gruff.libpath('bar')
28
28
  autoload :Bezier, Gruff.libpath('bezier')
29
- autoload :BoxPlot, Gruff.libpath('box_plot')
29
+ autoload :Box, Gruff.libpath('box')
30
+ autoload :Bubble, Gruff.libpath('bubble')
30
31
  autoload :Bullet, Gruff.libpath('bullet')
31
32
  autoload :Candlestick, Gruff.libpath('candlestick')
32
33
  autoload :Dot, Gruff.libpath('dot')
@@ -60,6 +61,7 @@ module Gruff
60
61
  class Store
61
62
  autoload :BasicData, Gruff.libpath('store/basic_data')
62
63
  autoload :XYData, Gruff.libpath('store/xy_data')
64
+ autoload :XYPointsizeData, Gruff.libpath('store/xy_pointsizes_data')
63
65
  end
64
66
 
65
67
  module Mini
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gruff
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.17.0
4
+ version: 0.18.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Geoffrey Grosenbach
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2022-06-12 00:00:00.000000000 Z
12
+ date: 2022-06-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rmagick
@@ -165,7 +165,8 @@ files:
165
165
  - lib/gruff/bar.rb
166
166
  - lib/gruff/base.rb
167
167
  - lib/gruff/bezier.rb
168
- - lib/gruff/box_plot.rb
168
+ - lib/gruff/box.rb
169
+ - lib/gruff/bubble.rb
169
170
  - lib/gruff/bullet.rb
170
171
  - lib/gruff/candlestick.rb
171
172
  - lib/gruff/dot.rb
@@ -204,6 +205,7 @@ files:
204
205
  - lib/gruff/store/basic_data.rb
205
206
  - lib/gruff/store/store.rb
206
207
  - lib/gruff/store/xy_data.rb
208
+ - lib/gruff/store/xy_pointsizes_data.rb
207
209
  - lib/gruff/themes.rb
208
210
  - lib/gruff/version.rb
209
211
  - rails_generators/gruff/gruff_generator.rb