gruff 0.5.1 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. checksums.yaml +5 -5
  2. data/.editorconfig +14 -0
  3. data/.github/ISSUE_TEMPLATE.md +18 -0
  4. data/.gitignore +3 -0
  5. data/.rubocop.yml +109 -0
  6. data/.rubocop_todo.yml +130 -0
  7. data/.travis.yml +24 -12
  8. data/.yardopts +1 -0
  9. data/{History.txt → CHANGELOG.md} +61 -25
  10. data/Gemfile +3 -7
  11. data/README.md +57 -25
  12. data/Rakefile +6 -201
  13. data/assets/plastik/blue.png +0 -0
  14. data/assets/plastik/green.png +0 -0
  15. data/assets/plastik/red.png +0 -0
  16. data/docker/Dockerfile +14 -0
  17. data/docker/build.sh +4 -0
  18. data/docker/launch.sh +4 -0
  19. data/gruff.gemspec +19 -14
  20. data/init.rb +2 -0
  21. data/lib/gruff.rb +26 -2
  22. data/lib/gruff/accumulator_bar.rb +18 -8
  23. data/lib/gruff/area.rb +33 -19
  24. data/lib/gruff/bar.rb +76 -45
  25. data/lib/gruff/base.rb +337 -613
  26. data/lib/gruff/bezier.rb +34 -19
  27. data/lib/gruff/bullet.rb +51 -62
  28. data/lib/gruff/dot.rb +38 -62
  29. data/lib/gruff/helper/bar_conversion.rb +47 -0
  30. data/lib/gruff/helper/bar_value_label_mixin.rb +30 -0
  31. data/lib/gruff/helper/stacked_mixin.rb +23 -0
  32. data/lib/gruff/histogram.rb +59 -0
  33. data/lib/gruff/line.rb +130 -150
  34. data/lib/gruff/mini/bar.rb +17 -10
  35. data/lib/gruff/mini/legend.rb +24 -36
  36. data/lib/gruff/mini/pie.rb +18 -12
  37. data/lib/gruff/mini/side_bar.rb +26 -12
  38. data/lib/gruff/net.rb +60 -84
  39. data/lib/gruff/patch/rmagick.rb +33 -0
  40. data/lib/gruff/patch/string.rb +10 -0
  41. data/lib/gruff/photo_bar.rb +27 -30
  42. data/lib/gruff/pie.rb +190 -93
  43. data/lib/gruff/renderer/bezier.rb +21 -0
  44. data/lib/gruff/renderer/circle.rb +21 -0
  45. data/lib/gruff/renderer/dash_line.rb +22 -0
  46. data/lib/gruff/renderer/dot.rb +39 -0
  47. data/lib/gruff/renderer/ellipse.rb +21 -0
  48. data/lib/gruff/renderer/line.rb +34 -0
  49. data/lib/gruff/renderer/polygon.rb +23 -0
  50. data/lib/gruff/renderer/polyline.rb +21 -0
  51. data/lib/gruff/renderer/rectangle.rb +19 -0
  52. data/lib/gruff/renderer/renderer.rb +127 -0
  53. data/lib/gruff/renderer/text.rb +42 -0
  54. data/lib/gruff/scatter.rb +156 -180
  55. data/lib/gruff/scene.rb +31 -41
  56. data/lib/gruff/side_bar.rb +77 -63
  57. data/lib/gruff/side_stacked_bar.rb +77 -60
  58. data/lib/gruff/spider.rb +37 -50
  59. data/lib/gruff/stacked_area.rb +32 -30
  60. data/lib/gruff/stacked_bar.rb +87 -49
  61. data/lib/gruff/store/base_data.rb +34 -0
  62. data/lib/gruff/store/custom_data.rb +34 -0
  63. data/lib/gruff/store/store.rb +80 -0
  64. data/lib/gruff/store/xy_data.rb +55 -0
  65. data/lib/gruff/themes.rb +32 -33
  66. data/lib/gruff/version.rb +3 -1
  67. metadata +99 -92
  68. data/Manifest.txt +0 -81
  69. data/RELEASE.md +0 -30
  70. data/assets/bubble.png +0 -0
  71. data/assets/city_scene/background/0000.png +0 -0
  72. data/assets/city_scene/background/0600.png +0 -0
  73. data/assets/city_scene/background/2000.png +0 -0
  74. data/assets/city_scene/clouds/cloudy.png +0 -0
  75. data/assets/city_scene/clouds/partly_cloudy.png +0 -0
  76. data/assets/city_scene/clouds/stormy.png +0 -0
  77. data/assets/city_scene/grass/default.png +0 -0
  78. data/assets/city_scene/haze/true.png +0 -0
  79. data/assets/city_scene/number_sample/1.png +0 -0
  80. data/assets/city_scene/number_sample/2.png +0 -0
  81. data/assets/city_scene/number_sample/default.png +0 -0
  82. data/assets/city_scene/sky/0000.png +0 -0
  83. data/assets/city_scene/sky/0200.png +0 -0
  84. data/assets/city_scene/sky/0400.png +0 -0
  85. data/assets/city_scene/sky/0600.png +0 -0
  86. data/assets/city_scene/sky/0800.png +0 -0
  87. data/assets/city_scene/sky/1000.png +0 -0
  88. data/assets/city_scene/sky/1200.png +0 -0
  89. data/assets/city_scene/sky/1400.png +0 -0
  90. data/assets/city_scene/sky/1500.png +0 -0
  91. data/assets/city_scene/sky/1700.png +0 -0
  92. data/assets/city_scene/sky/2000.png +0 -0
  93. data/assets/pc306715.jpg +0 -0
  94. data/lib/gruff/bar_conversion.rb +0 -46
  95. data/lib/gruff/deprecated.rb +0 -39
  96. data/lib/gruff/stacked_mixin.rb +0 -23
  97. data/test/gruff_test_case.rb +0 -154
  98. data/test/image_compare.rb +0 -58
  99. data/test/test_accumulator_bar.rb +0 -51
  100. data/test/test_area.rb +0 -134
  101. data/test/test_bar.rb +0 -505
  102. data/test/test_base.rb +0 -8
  103. data/test/test_bezier.rb +0 -33
  104. data/test/test_bullet.rb +0 -26
  105. data/test/test_dot.rb +0 -263
  106. data/test/test_legend.rb +0 -68
  107. data/test/test_line.rb +0 -657
  108. data/test/test_mini_bar.rb +0 -33
  109. data/test/test_mini_pie.rb +0 -25
  110. data/test/test_mini_side_bar.rb +0 -36
  111. data/test/test_net.rb +0 -231
  112. data/test/test_photo.rb +0 -41
  113. data/test/test_pie.rb +0 -154
  114. data/test/test_scatter.rb +0 -233
  115. data/test/test_scene.rb +0 -100
  116. data/test/test_side_bar.rb +0 -56
  117. data/test/test_sidestacked_bar.rb +0 -105
  118. data/test/test_spider.rb +0 -226
  119. data/test/test_stacked_area.rb +0 -52
  120. data/test/test_stacked_bar.rb +0 -52
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @private
4
+ module Gruff::Base::StackedMixin
5
+ # Used by StackedBar and child classes.
6
+ #
7
+ # tsal: moved from Base 03 FEB 2007
8
+ def calculate_maximum_by_stack
9
+ # Get sum of each stack
10
+ max_hash = {}
11
+ store.data.each do |data_set|
12
+ data_set.points.each_with_index do |data_point, i|
13
+ max_hash[i] = 0.0 unless max_hash[i]
14
+ max_hash[i] += data_point.to_f
15
+ end
16
+ end
17
+
18
+ max_hash.each_key do |key|
19
+ self.maximum_value = max_hash[key] if max_hash[key] > maximum_value
20
+ end
21
+ self.minimum_value = 0
22
+ end
23
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'histogram'
4
+ require 'gruff/base'
5
+
6
+ #
7
+ # Here's how to set up a Gruff::Histogram.
8
+ #
9
+ # g = Gruff::Histogram.new
10
+ # g.title = 'Histogram Graph'
11
+ # g.minimum_bin = 10
12
+ # g.bin_width = 20
13
+ # g.data :A, [10, 10, 20, 30, 40, 40, 40, 40, 40, 40, 50, 10, 10, 10]
14
+ # g.data :B, [100, 100, 100, 100, 90, 90, 80, 30, 30, 30, 30, 30]
15
+ # g.write('histogram.png')
16
+ #
17
+ class Gruff::Histogram < Gruff::Bar
18
+ def initialize_ivars
19
+ super
20
+ @bin_width = 10
21
+ @minimum_bin = nil
22
+ @maximum_bin = nil
23
+ end
24
+ private :initialize_ivars
25
+
26
+ # Specifies interpolation between the min and max of the set. Default is +10+.
27
+ def bin_width=(width)
28
+ raise 'bin_width= should be called before set the data.' unless store.empty?
29
+
30
+ @bin_width = width
31
+ end
32
+
33
+ # Specifies minimum value for bin.
34
+ def minimum_bin=(min)
35
+ raise 'minimum_bin= should be called before set the data.' unless store.empty?
36
+
37
+ @minimum_bin = min
38
+ end
39
+
40
+ # Specifies maximum value for bin.
41
+ def maximum_bin=(max)
42
+ raise 'maximum_bin= should be called before set the data.' unless store.empty?
43
+
44
+ @maximum_bin = max
45
+ end
46
+
47
+ def data(name, data_points = [], color = nil)
48
+ bins, freqs = HistogramArray.new(data_points).histogram(bin_width: @bin_width, min: @minimum_bin, max: @maximum_bin)
49
+ bins.each_with_index do |bin, index|
50
+ labels[index] = bin
51
+ end
52
+ store.add(name, freqs, color)
53
+ end
54
+
55
+ # @private
56
+ class HistogramArray < Array
57
+ include ::Histogram
58
+ end
59
+ end
@@ -1,163 +1,156 @@
1
- require File.dirname(__FILE__) + '/base'
1
+ # frozen_string_literal: true
2
2
 
3
- ##
4
- # Here's how to make a Line graph:
3
+ require 'gruff/base'
4
+
5
+ #
6
+ # Here's how to make a Gruff::Line.
5
7
  #
6
8
  # g = Gruff::Line.new
7
9
  # g.title = "A Line Graph"
8
10
  # g.data 'Fries', [20, 23, 19, 8]
9
11
  # g.data 'Hamburgers', [50, 19, 99, 29]
10
- # g.write("test/output/line.png")
12
+ # g.write("line.png")
13
+ #
14
+ # There are also other options described below, such as {#baseline_value}, {#baseline_color},
15
+ # {#hide_dots}, and {#hide_lines}.
11
16
  #
12
- # There are also other options described below, such as #baseline_value, #baseline_color, #hide_dots, and #hide_lines.
13
-
14
17
  class Gruff::Line < Gruff::Base
15
-
16
- # Allow for reference lines ( which are like baseline ... just allowing for more & on both axes )
18
+ # Allow for reference lines ( which are like baseline ... just allowing for more & on both axes ).
17
19
  attr_accessor :reference_lines
18
20
  attr_accessor :reference_line_default_color
19
21
  attr_accessor :reference_line_default_width
20
22
 
21
- # Dimensions of lines and dots; calculated based on dataset size if left unspecified
23
+ # Allow for vertical marker lines.
24
+ attr_accessor :show_vertical_markers
25
+
26
+ # Dimensions of lines and dots; calculated based on dataset size if left unspecified.
22
27
  attr_accessor :line_width
23
28
  attr_accessor :dot_radius
24
29
 
30
+ # default is +'circle'+, other options include square.
31
+ attr_accessor :dot_style
32
+
25
33
  # Hide parts of the graph to fit more datapoints, or for a different appearance.
26
34
  attr_accessor :hide_dots, :hide_lines
27
35
 
28
- #accessors for support of xy data
36
+ # accessors for support of xy data.
29
37
  attr_accessor :minimum_x_value
38
+
39
+ # accessors for support of xy data.
30
40
  attr_accessor :maximum_x_value
31
41
 
32
42
  # Get the value if somebody has defined it.
33
43
  def baseline_value
34
- if (@reference_lines.key?(:baseline))
44
+ if @reference_lines.key?(:baseline)
35
45
  @reference_lines[:baseline][:value]
36
- else
37
- nil
38
46
  end
39
47
  end
40
48
 
41
49
  # Set a value for a baseline reference line..
42
50
  def baseline_value=(new_value)
43
- @reference_lines[:baseline] ||= Hash.new
51
+ @reference_lines[:baseline] ||= {}
44
52
  @reference_lines[:baseline][:value] = new_value
45
53
  end
46
54
 
47
55
  def baseline_color
48
- if (@reference_lines.key?(:baseline))
56
+ if @reference_lines.key?(:baseline)
49
57
  @reference_lines[:baseline][:color]
50
- else
51
- nil
52
58
  end
53
59
  end
54
60
 
55
61
  def baseline_color=(new_value)
56
- @reference_lines[:baseline] ||= Hash.new
62
+ @reference_lines[:baseline] ||= {}
57
63
  @reference_lines[:baseline][:color] = new_value
58
64
  end
59
65
 
60
- # Call with target pixel width of graph (800, 400, 300), and/or 'false' to omit lines (points only).
61
- #
62
- # g = Gruff::Line.new(400) # 400px wide with lines
66
+ # Call with target pixel width of graph (+800+, +400+, +300+), and/or +false+ to omit lines (points only).
63
67
  #
64
- # g = Gruff::Line.new(400, false) # 400px wide, no lines (for backwards compatibility)
68
+ # g = Gruff::Line.new(400) # 400px wide with lines
69
+ # g = Gruff::Line.new(400, false) # 400px wide, no lines (for backwards compatibility)
70
+ # g = Gruff::Line.new(false) # Defaults to 800px wide, no lines (for backwards compatibility)
65
71
  #
66
- # g = Gruff::Line.new(false) # Defaults to 800px wide, no lines (for backwards compatibility)
67
- #
68
- # The preferred way is to call hide_dots or hide_lines instead.
72
+ # The preferred way is to call {#hide_dots} or {#hide_lines} instead.
69
73
  def initialize(*args)
70
74
  raise ArgumentError, 'Wrong number of arguments' if args.length > 2
71
- if args.empty? || ((not Numeric === args.first) && (not String === args.first))
75
+
76
+ if args.empty? || (!args.first.is_a?(Numeric) && !args.first.is_a?(String))
72
77
  super()
73
78
  else
74
79
  super args.shift
75
80
  end
76
81
 
77
- @reference_lines = Hash.new
82
+ @reference_lines = {}
78
83
  @reference_line_default_color = 'red'
79
84
  @reference_line_default_width = 5
80
85
 
81
86
  @hide_dots = @hide_lines = false
82
87
  @maximum_x_value = nil
83
88
  @minimum_x_value = nil
89
+
90
+ @dot_style = 'circle'
91
+
92
+ @show_vertical_markers = false
93
+
94
+ @store = Gruff::Store.new(Gruff::Store::XYData)
84
95
  end
85
96
 
86
97
  # This method allows one to plot a dataset with both X and Y data.
87
98
  #
88
- # Parameters are as follows:
89
- # name: string, the title of the dataset
90
- # x_data_points: an array containing the x data points for the graph
91
- # y_data_points: an array containing the y data points for the graph
92
- # color: hex number indicating the line color as an RGB triplet
93
- #
94
- # or
99
+ # @overload dataxy(name, x_data_points = [], y_data_points = [], color = nil)
100
+ # @param name [String] the title of the dataset.
101
+ # @param x_data_points [Array] an array containing the x data points for the graph.
102
+ # @param y_data_points [Array] an array containing the y data points for the graph.
103
+ # @param color [String] hex number indicating the line color as an RGB triplet.
95
104
  #
96
- # name: string, the title of the dataset
97
- # xy_data_points: an array containing both x and y data points for the graph
98
- # color: hex number indicating the line color as an RGB triplet
105
+ # @overload dataxy(name, xy_data_points = [], color = nil)
106
+ # @param name [String] the title of the dataset.
107
+ # @param xy_data_points [Array] an array containing both x and y data points for the graph.
108
+ # @param color [String] hex number indicating the line color as an RGB triplet.
99
109
  #
100
- # Notes:
101
- # -if (x_data_points.length != y_data_points.length) an error is
110
+ # @note
111
+ # - if (x_data_points.length != y_data_points.length) an error is
102
112
  # returned.
103
- # -if the color argument is nil, the next color from the default theme will
113
+ # - if the color argument is nil, the next color from the default theme will
104
114
  # be used.
105
- # -if you want to use a preset theme, you must set it before calling
106
- # dataxy().
115
+ # - if you want to use a preset theme, you must set it before calling {#dataxy}.
107
116
  #
108
- # Example:
117
+ # @example
109
118
  # g = Gruff::Line.new
110
119
  # g.title = "X/Y Dataset"
111
120
  # g.dataxy("Apples", [1,3,4,5,6,10], [1, 2, 3, 4, 4, 3])
112
121
  # g.dataxy("Bapples", [1,3,4,5,7,9], [1, 1, 2, 2, 3, 3])
113
122
  # g.dataxy("Capples", [[1,1],[2,3],[3,4],[4,5],[5,7],[6,9]])
114
- # #you can still use the old data method too if you want:
115
- # g.data("Capples", [1, 1, 2, 2, 3, 3])
116
- # #labels will be drawn at the x locations of the keys passed in.
123
+ #
124
+ # # you can still use the old data method too if you want:
125
+ # g.data("Capples", [1, 1, 2, 2, 3, 3])
126
+ #
127
+ # # labels will be drawn at the x locations of the keys passed in.
117
128
  # In this example the lables are drawn at x positions 2, 4, and 6:
118
129
  # g.labels = {0 => '2003', 2 => '2004', 4 => '2005', 6 => '2006'}
119
- # The 0 => '2003' label will be ignored since it is outside the chart range.
120
- def dataxy(name, x_data_points=[], y_data_points=[], color=nil)
121
- raise ArgumentError, 'x_data_points is nil!' if x_data_points.length == 0
130
+ # # The 0 => '2003' label will be ignored since it is outside the chart range.
131
+ def dataxy(name, x_data_points = [], y_data_points = [], color = nil)
132
+ # make sure it's an array
133
+ x_data_points = Array(x_data_points)
134
+ y_data_points = Array(y_data_points)
135
+
136
+ raise ArgumentError, 'x_data_points is nil!' if x_data_points.empty?
122
137
 
123
138
  if x_data_points.all? { |p| p.is_a?(Array) && p.size == 2 }
124
- x_data_points, y_data_points = x_data_points.map { |p| p[0] }, x_data_points.map { |p| p[1] }
139
+ x_data_points, y_data_points = x_data_points.transpose
125
140
  end
126
141
 
127
142
  raise ArgumentError, 'x_data_points.length != y_data_points.length!' if x_data_points.length != y_data_points.length
128
143
 
129
- # call the existing data routine for the y data.
130
- self.data(name, y_data_points, color)
131
-
132
- x_data_points = Array(x_data_points) # make sure it's an array
133
- # append the x data to the last entry that was just added in the @data member
134
- @data.last[DATA_VALUES_X_INDEX] = x_data_points
135
-
136
- # Update the global min/max values for the x data
137
- x_data_points.each do |x_data_point|
138
- next if x_data_point.nil?
139
-
140
- # Setup max/min so spread starts at the low end of the data points
141
- if @maximum_x_value.nil? && @minimum_x_value.nil?
142
- @maximum_x_value = @minimum_x_value = x_data_point
143
- end
144
-
145
- @maximum_x_value = (x_data_point > @maximum_x_value) ?
146
- x_data_point : @maximum_x_value
147
- @minimum_x_value = (x_data_point < @minimum_x_value) ?
148
- x_data_point : @minimum_x_value
149
- end
150
-
144
+ # call the existing data routine for the x/y data.
145
+ store.add(name, y_data_points, color, x_data_points)
151
146
  end
152
147
 
153
148
  def draw_reference_line(reference_line, left, right, top, bottom)
154
- @d = @d.push
155
- @d.stroke_color(reference_line[:color] || @reference_line_default_color)
156
- @d.fill_opacity 0.0
157
- @d.stroke_dasharray(10, 20)
158
- @d.stroke_width(reference_line[:width] || @reference_line_default_width)
159
- @d.line(left, top, right, bottom)
160
- @d = @d.pop
149
+ config = {
150
+ color: reference_line[:color] || @reference_line_default_color,
151
+ width: reference_line[:width] || @reference_line_default_width
152
+ }
153
+ Gruff::Renderer::DashLine.new(config).render(left, top, right, bottom)
161
154
  end
162
155
 
163
156
  def draw_horizontal_reference_line(reference_line)
@@ -167,118 +160,118 @@ class Gruff::Line < Gruff::Base
167
160
 
168
161
  def draw_vertical_reference_line(reference_line)
169
162
  index = @graph_left + (@x_increment * reference_line[:index])
170
- draw_reference_line(reference_line, index, index, @graph_top, @graph_top + graph_height)
163
+ draw_reference_line(reference_line, index, index, @graph_top, @graph_top + @graph_height)
171
164
  end
172
165
 
173
166
  def draw
174
167
  super
175
168
 
176
- return unless @has_data
169
+ return unless data_given?
177
170
 
178
- # Check to see if more than one datapoint was given. NaN can result otherwise.
179
- @x_increment = (@column_count > 1) ? (@graph_width / (@column_count - 1).to_f) : @graph_width
171
+ # Check to see if more than one datapoint was given. NaN can result otherwise.
172
+ @x_increment = (column_count > 1) ? (@graph_width / (column_count - 1).to_f) : @graph_width
180
173
 
181
174
  @reference_lines.each_value do |curr_reference_line|
182
175
  draw_horizontal_reference_line(curr_reference_line) if curr_reference_line.key?(:norm_value)
183
176
  draw_vertical_reference_line(curr_reference_line) if curr_reference_line.key?(:index)
184
177
  end
185
178
 
186
- @norm_data.each do |data_row|
179
+ if @show_vertical_markers
180
+ (0..column_count).each do |column|
181
+ x = @graph_left + @graph_width - column.to_f * @x_increment
182
+
183
+ Gruff::Renderer::Line.new(color: @marker_color).render(x, @graph_bottom, x, @graph_top)
184
+ #If the user specified a marker shadow color, draw a shadow just below it
185
+ if @marker_shadow_color
186
+ Gruff::Renderer::Line.new(color: @marker_shadow_color).render(x + 1, @graph_bottom, x + 1, @graph_top)
187
+ end
188
+ end
189
+ end
190
+
191
+ store.norm_data.each do |data_row|
187
192
  prev_x = prev_y = nil
188
193
 
189
- @one_point = contains_one_point_only?(data_row)
194
+ one_point = contains_one_point_only?(data_row)
190
195
 
191
- data_row[DATA_VALUES_INDEX].each_with_index do |data_point, index|
192
- unless data_point
193
- prev_x = prev_y = nil
194
- next
195
- end
196
- x_data = data_row[DATA_VALUES_X_INDEX]
197
- if x_data == nil
196
+ data_row.coordinates.each_with_index do |(x_data, y_data), index|
197
+ if x_data.nil?
198
198
  #use the old method: equally spaced points along the x-axis
199
199
  new_x = @graph_left + (@x_increment * index)
200
200
  draw_label(new_x, index)
201
201
  else
202
- new_x = get_x_coord(x_data[index], @graph_width, @graph_left)
202
+ new_x = get_x_coord(x_data, @graph_width, @graph_left)
203
203
  @labels.each do |label_pos, _|
204
204
  draw_label(@graph_left + ((label_pos - @minimum_x_value) * @graph_width) / (@maximum_x_value - @minimum_x_value), label_pos)
205
205
  end
206
206
  end
207
+ unless y_data # we can't draw a line for a null data point, we can still label the axis though
208
+ prev_x = prev_y = nil
209
+ next
210
+ end
207
211
 
208
- new_y = @graph_top + (@graph_height - data_point * @graph_height)
212
+ new_y = @graph_top + (@graph_height - y_data * @graph_height)
209
213
 
210
214
  # Reset each time to avoid thin-line errors
211
- @d = @d.stroke data_row[DATA_COLOR_INDEX]
212
- @d = @d.fill data_row[DATA_COLOR_INDEX]
213
- @d = @d.stroke_opacity 1.0
214
- @d = @d.stroke_width line_width ||
215
- clip_value_if_greater_than(@columns / (@norm_data.first[DATA_VALUES_INDEX].size * 4), 5.0)
216
-
217
- circle_radius = dot_radius ||
218
- clip_value_if_greater_than(@columns / (@norm_data.first[DATA_VALUES_INDEX].size * 2.5), 5.0)
219
-
220
- if !@hide_lines && !prev_x.nil? && !prev_y.nil?
221
- @d = @d.line(prev_x, prev_y, new_x, new_y)
222
- elsif @one_point
223
- # Show a circle if there's just one_point
224
- @d = @d.circle(new_x, new_y, new_x - circle_radius, new_y)
215
+ stroke_width = line_width || clip_value_if_greater_than(@columns / (store.norm_data.first.y_points.size * 4), 5.0)
216
+ circle_radius = dot_radius || clip_value_if_greater_than(@columns / (store.norm_data.first.y_points.size * 2.5), 5.0)
217
+
218
+ if !@hide_lines && prev_x && prev_y
219
+ Gruff::Renderer::Line.new(color: data_row.color, width: stroke_width)
220
+ .render(prev_x, prev_y, new_x, new_y)
225
221
  end
226
- @d = @d.circle(new_x, new_y, new_x - circle_radius, new_y) unless @hide_dots
227
222
 
228
- prev_x, prev_y = new_x, new_y
223
+ if one_point || !@hide_dots
224
+ Gruff::Renderer::Dot.new(@dot_style, color: data_row.color, width: stroke_width).render(new_x, new_y, circle_radius)
225
+ end
226
+
227
+ prev_x = new_x
228
+ prev_y = new_y
229
229
  end
230
230
  end
231
231
 
232
- @d.draw(@base_image)
232
+ Gruff::Renderer.finish
233
233
  end
234
234
 
235
+ private
236
+
235
237
  def setup_data
238
+ # Update the global min/max values for the x data
239
+ @maximum_x_value ||= store.max_x
240
+ @minimum_x_value ||= store.min_x
236
241
 
237
242
  # Deal with horizontal reference line values that exceed the existing minimum & maximum values.
238
- possible_maximums = [@maximum_value.to_f]
239
- possible_minimums = [@minimum_value.to_f]
243
+ possible_maximums = [maximum_value.to_f]
244
+ possible_minimums = [minimum_value.to_f]
240
245
 
241
246
  @reference_lines.each_value do |curr_reference_line|
242
- if (curr_reference_line.key?(:value))
247
+ if curr_reference_line.key?(:value)
243
248
  possible_maximums << curr_reference_line[:value].to_f
244
249
  possible_minimums << curr_reference_line[:value].to_f
245
250
  end
246
251
  end
247
252
 
248
- @maximum_value = possible_maximums.max
249
- @minimum_value = possible_minimums.min
253
+ self.maximum_value = possible_maximums.max
254
+ self.minimum_value = possible_minimums.min
250
255
 
251
256
  super
252
257
  end
253
258
 
254
- def normalize(force=false)
255
- super(force)
259
+ def normalize
260
+ return unless data_given?
256
261
 
257
- @reference_lines.each_value do |curr_reference_line|
262
+ spread_x = @maximum_x_value.to_f - @minimum_x_value.to_f
263
+ store.normalize(minimum_x: @minimum_x_value, spread_x: spread_x, minimum_y: minimum_value, spread_y: @spread)
258
264
 
259
- # We only care about horizontal markers ... for normalization.
265
+ @reference_lines.each_value do |curr_reference_line|
266
+ # We only care about horizontal markers ... for normalization.
260
267
  # Vertical markers won't have a :value, they will have an :index
261
268
 
262
- curr_reference_line[:norm_value] = ((curr_reference_line[:value].to_f - @minimum_value) / @spread.to_f) if (curr_reference_line.key?(:value))
263
-
269
+ curr_reference_line[:norm_value] = ((curr_reference_line[:value].to_f - minimum_value) / @spread.to_f) if curr_reference_line.key?(:value)
264
270
  end
265
-
266
- #normalize the x data if it is specified
267
- @data.each_with_index do |data_row, index|
268
- norm_x_data_points = []
269
- if data_row[DATA_VALUES_X_INDEX] != nil
270
- data_row[DATA_VALUES_X_INDEX].each do |x_data_point|
271
- norm_x_data_points << ((x_data_point.to_f - @minimum_x_value.to_f) /
272
- (@maximum_x_value.to_f - @minimum_x_value.to_f))
273
- end
274
- @norm_data[index] << norm_x_data_points
275
- end
276
- end
277
-
278
271
  end
279
272
 
280
273
  def sort_norm_data
281
- super unless @data.any? { |d| d[DATA_VALUES_X_INDEX] }
274
+ super unless store.data.any?(&:x_points)
282
275
  end
283
276
 
284
277
  def get_x_coord(x_data_point, width, offset)
@@ -286,19 +279,6 @@ class Gruff::Line < Gruff::Base
286
279
  end
287
280
 
288
281
  def contains_one_point_only?(data_row)
289
- # Spin through data to determine if there is just one_value present.
290
- one_point = false
291
- data_row[DATA_VALUES_INDEX].each do |data_point|
292
- unless data_point.nil?
293
- if one_point
294
- # more than one point, bail
295
- return false
296
- end
297
- # there is at least one data point
298
- one_point = true
299
- end
300
- end
301
- one_point
282
+ data_row.y_points.compact.count == 1
302
283
  end
303
-
304
284
  end