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