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.
- checksums.yaml +5 -5
- data/{History.txt → CHANGELOG.md} +81 -25
- data/Gemfile +3 -1
- data/README.md +51 -23
- data/assets/plastik/blue.png +0 -0
- data/assets/plastik/green.png +0 -0
- data/assets/plastik/red.png +0 -0
- data/gruff.gemspec +23 -13
- data/init.rb +2 -0
- data/lib/gruff.rb +33 -4
- data/lib/gruff/accumulator_bar.rb +17 -9
- data/lib/gruff/area.rb +31 -21
- data/lib/gruff/bar.rb +90 -46
- data/lib/gruff/base.rb +476 -710
- data/lib/gruff/bezier.rb +29 -18
- data/lib/gruff/bullet.rb +58 -71
- data/lib/gruff/dot.rb +35 -83
- data/lib/gruff/helper/bar_conversion.rb +47 -0
- data/lib/gruff/helper/bar_value_label_mixin.rb +33 -0
- data/lib/gruff/helper/stacked_mixin.rb +23 -0
- data/lib/gruff/histogram.rb +59 -0
- data/lib/gruff/line.rb +121 -199
- data/lib/gruff/mini/bar.rb +17 -10
- data/lib/gruff/mini/legend.rb +26 -38
- data/lib/gruff/mini/pie.rb +18 -13
- data/lib/gruff/mini/side_bar.rb +25 -12
- data/lib/gruff/net.rb +69 -83
- data/lib/gruff/patch/rmagick.rb +31 -0
- data/lib/gruff/patch/string.rb +13 -0
- data/lib/gruff/photo_bar.rb +36 -43
- data/lib/gruff/pie.rb +42 -103
- data/lib/gruff/renderer/bezier.rb +22 -0
- data/lib/gruff/renderer/circle.rb +22 -0
- data/lib/gruff/renderer/dash_line.rb +23 -0
- data/lib/gruff/renderer/dot.rb +40 -0
- data/lib/gruff/renderer/ellipse.rb +22 -0
- data/lib/gruff/renderer/line.rb +43 -0
- data/lib/gruff/renderer/polygon.rb +24 -0
- data/lib/gruff/renderer/polyline.rb +22 -0
- data/lib/gruff/renderer/rectangle.rb +20 -0
- data/lib/gruff/renderer/renderer.rb +120 -0
- data/lib/gruff/renderer/text.rb +57 -0
- data/lib/gruff/scatter.rb +128 -201
- data/lib/gruff/scene.rb +30 -41
- data/lib/gruff/side_bar.rb +100 -68
- data/lib/gruff/side_stacked_bar.rb +92 -63
- data/lib/gruff/spider.rb +47 -53
- data/lib/gruff/stacked_area.rb +37 -34
- data/lib/gruff/stacked_bar.rb +99 -54
- data/lib/gruff/store/basic_data.rb +36 -0
- data/lib/gruff/store/custom_data.rb +36 -0
- data/lib/gruff/store/store.rb +81 -0
- data/lib/gruff/store/xy_data.rb +58 -0
- data/lib/gruff/themes.rb +32 -33
- data/lib/gruff/version.rb +3 -1
- metadata +74 -102
- data/.gitignore +0 -7
- data/.travis.yml +0 -19
- data/Manifest.txt +0 -81
- data/RELEASE.md +0 -30
- data/Rakefile +0 -218
- data/assets/bubble.png +0 -0
- data/assets/city_scene/background/0000.png +0 -0
- data/assets/city_scene/background/0600.png +0 -0
- data/assets/city_scene/background/2000.png +0 -0
- data/assets/city_scene/clouds/cloudy.png +0 -0
- data/assets/city_scene/clouds/partly_cloudy.png +0 -0
- data/assets/city_scene/clouds/stormy.png +0 -0
- data/assets/city_scene/grass/default.png +0 -0
- data/assets/city_scene/haze/true.png +0 -0
- data/assets/city_scene/number_sample/1.png +0 -0
- data/assets/city_scene/number_sample/2.png +0 -0
- data/assets/city_scene/number_sample/default.png +0 -0
- data/assets/city_scene/sky/0000.png +0 -0
- data/assets/city_scene/sky/0200.png +0 -0
- data/assets/city_scene/sky/0400.png +0 -0
- data/assets/city_scene/sky/0600.png +0 -0
- data/assets/city_scene/sky/0800.png +0 -0
- data/assets/city_scene/sky/1000.png +0 -0
- data/assets/city_scene/sky/1200.png +0 -0
- data/assets/city_scene/sky/1400.png +0 -0
- data/assets/city_scene/sky/1500.png +0 -0
- data/assets/city_scene/sky/1700.png +0 -0
- data/assets/city_scene/sky/2000.png +0 -0
- data/assets/pc306715.jpg +0 -0
- data/lib/gruff/bar_conversion.rb +0 -46
- data/lib/gruff/deprecated.rb +0 -39
- data/lib/gruff/stacked_mixin.rb +0 -23
- data/test/gruff_test_case.rb +0 -152
- data/test/image_compare.rb +0 -58
- data/test/test_accumulator_bar.rb +0 -51
- data/test/test_area.rb +0 -134
- data/test/test_bar.rb +0 -505
- data/test/test_base.rb +0 -33
- data/test/test_bezier.rb +0 -33
- data/test/test_bullet.rb +0 -26
- data/test/test_dot.rb +0 -263
- data/test/test_labels_for_null_data.rb +0 -27
- data/test/test_legend.rb +0 -68
- data/test/test_line.rb +0 -674
- data/test/test_mini_bar.rb +0 -33
- data/test/test_mini_pie.rb +0 -25
- data/test/test_mini_side_bar.rb +0 -36
- data/test/test_net.rb +0 -231
- data/test/test_photo.rb +0 -41
- data/test/test_pie.rb +0 -194
- data/test/test_scatter.rb +0 -270
- data/test/test_scene.rb +0 -100
- data/test/test_side_bar.rb +0 -56
- data/test/test_sidestacked_bar.rb +0 -105
- data/test/test_spider.rb +0 -226
- data/test/test_stacked_area.rb +0 -52
- 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
|
data/lib/gruff/line.rb
CHANGED
@@ -1,86 +1,91 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
# Here's how to make a Line
|
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("
|
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
|
-
|
19
|
-
|
18
|
+
attr_writer :reference_line_default_color
|
19
|
+
attr_writer :reference_line_default_width
|
20
20
|
|
21
|
-
# Allow for vertical marker lines
|
22
|
-
|
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
|
-
|
26
|
-
|
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
|
29
|
-
|
28
|
+
# default is +'circle'+, other options include square.
|
29
|
+
attr_writer :dot_style
|
30
30
|
|
31
|
-
# Hide parts of the graph to fit more
|
32
|
-
|
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
|
-
|
36
|
-
|
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
|
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] ||=
|
49
|
+
@reference_lines[:baseline] ||= {}
|
50
50
|
@reference_lines[:baseline][:value] = new_value
|
51
51
|
end
|
52
52
|
|
53
53
|
def baseline_color
|
54
|
-
if
|
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] ||=
|
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
|
64
|
+
# Call with target pixel width of graph (+800+, +400+, +300+), and/or +false+ to omit lines (points only).
|
67
65
|
#
|
68
|
-
#
|
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
|
-
#
|
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
|
-
|
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
|
-
|
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
|
-
#
|
99
|
-
# name
|
100
|
-
# x_data_points
|
101
|
-
# y_data_points
|
102
|
-
# color
|
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
|
-
#
|
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
|
-
#
|
107
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
127
|
-
#
|
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
|
-
|
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.
|
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
|
-
|
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
|
-
|
165
|
-
|
166
|
-
|
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
|
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 = (
|
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
|
197
|
-
(0
|
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
|
-
|
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
|
-
|
212
|
-
|
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
|
-
|
196
|
+
store.norm_data.each do |data_row|
|
219
197
|
prev_x = prev_y = nil
|
220
198
|
|
221
|
-
|
199
|
+
one_point = contains_one_point_only?(data_row)
|
222
200
|
|
223
|
-
data_row
|
224
|
-
x_data
|
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
|
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
|
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 -
|
217
|
+
new_y = @graph_top + (@graph_height - y_data * @graph_height)
|
241
218
|
|
242
219
|
# Reset each time to avoid thin-line errors
|
243
|
-
@
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
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
|
-
|
260
|
-
@
|
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
|
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 = [
|
274
|
-
possible_minimums = [
|
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
|
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
|
-
|
284
|
-
|
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
|
290
|
-
|
262
|
+
def normalize
|
263
|
+
return unless data_given?
|
291
264
|
|
292
|
-
@
|
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 -
|
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
|
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
|
-
|
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
|