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