gruff 0.5.1-java → 0.10.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/.editorconfig +14 -0
- data/.github/ISSUE_TEMPLATE.md +18 -0
- data/.gitignore +3 -0
- data/.rubocop.yml +109 -0
- data/.rubocop_todo.yml +130 -0
- data/.travis.yml +24 -12
- data/.yardopts +1 -0
- data/{History.txt → CHANGELOG.md} +61 -25
- data/Gemfile +3 -7
- data/README.md +57 -25
- data/Rakefile +6 -201
- data/assets/plastik/blue.png +0 -0
- data/assets/plastik/green.png +0 -0
- data/assets/plastik/red.png +0 -0
- data/docker/Dockerfile +14 -0
- data/docker/build.sh +4 -0
- data/docker/launch.sh +4 -0
- data/gruff.gemspec +19 -14
- data/init.rb +2 -0
- data/lib/gruff.rb +26 -2
- data/lib/gruff/accumulator_bar.rb +18 -8
- data/lib/gruff/area.rb +33 -19
- data/lib/gruff/bar.rb +76 -45
- data/lib/gruff/base.rb +337 -613
- data/lib/gruff/bezier.rb +34 -19
- data/lib/gruff/bullet.rb +51 -62
- data/lib/gruff/dot.rb +38 -62
- data/lib/gruff/helper/bar_conversion.rb +47 -0
- data/lib/gruff/helper/bar_value_label_mixin.rb +30 -0
- data/lib/gruff/helper/stacked_mixin.rb +23 -0
- data/lib/gruff/histogram.rb +59 -0
- data/lib/gruff/line.rb +130 -150
- data/lib/gruff/mini/bar.rb +17 -10
- data/lib/gruff/mini/legend.rb +24 -36
- data/lib/gruff/mini/pie.rb +18 -12
- data/lib/gruff/mini/side_bar.rb +26 -12
- data/lib/gruff/net.rb +60 -84
- data/lib/gruff/patch/rmagick.rb +33 -0
- data/lib/gruff/patch/string.rb +10 -0
- data/lib/gruff/photo_bar.rb +27 -30
- data/lib/gruff/pie.rb +190 -93
- data/lib/gruff/renderer/bezier.rb +21 -0
- data/lib/gruff/renderer/circle.rb +21 -0
- data/lib/gruff/renderer/dash_line.rb +22 -0
- data/lib/gruff/renderer/dot.rb +39 -0
- data/lib/gruff/renderer/ellipse.rb +21 -0
- data/lib/gruff/renderer/line.rb +34 -0
- data/lib/gruff/renderer/polygon.rb +23 -0
- data/lib/gruff/renderer/polyline.rb +21 -0
- data/lib/gruff/renderer/rectangle.rb +19 -0
- data/lib/gruff/renderer/renderer.rb +127 -0
- data/lib/gruff/renderer/text.rb +42 -0
- data/lib/gruff/scatter.rb +156 -180
- data/lib/gruff/scene.rb +31 -41
- data/lib/gruff/side_bar.rb +77 -63
- data/lib/gruff/side_stacked_bar.rb +77 -60
- data/lib/gruff/spider.rb +37 -50
- data/lib/gruff/stacked_area.rb +32 -30
- data/lib/gruff/stacked_bar.rb +87 -49
- data/lib/gruff/store/base_data.rb +34 -0
- data/lib/gruff/store/custom_data.rb +34 -0
- data/lib/gruff/store/store.rb +80 -0
- data/lib/gruff/store/xy_data.rb +55 -0
- data/lib/gruff/themes.rb +32 -33
- data/lib/gruff/version.rb +3 -1
- metadata +88 -94
- data/Manifest.txt +0 -81
- data/RELEASE.md +0 -30
- 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 -154
- 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 -8
- data/test/test_bezier.rb +0 -33
- data/test/test_bullet.rb +0 -26
- data/test/test_dot.rb +0 -263
- data/test/test_legend.rb +0 -68
- data/test/test_line.rb +0 -657
- 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 -154
- data/test/test_scatter.rb +0 -233
- 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 -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
|
data/lib/gruff/line.rb
CHANGED
|
@@ -1,163 +1,156 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
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("
|
|
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
|
-
#
|
|
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
|
|
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] ||=
|
|
51
|
+
@reference_lines[:baseline] ||= {}
|
|
44
52
|
@reference_lines[:baseline][:value] = new_value
|
|
45
53
|
end
|
|
46
54
|
|
|
47
55
|
def baseline_color
|
|
48
|
-
if
|
|
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] ||=
|
|
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
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
-
|
|
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 =
|
|
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
|
-
#
|
|
89
|
-
# name
|
|
90
|
-
# x_data_points
|
|
91
|
-
# y_data_points
|
|
92
|
-
# color
|
|
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
|
-
#
|
|
97
|
-
#
|
|
98
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
115
|
-
#
|
|
116
|
-
#
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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
|
|
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 = (
|
|
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
|
-
@
|
|
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
|
-
|
|
194
|
+
one_point = contains_one_point_only?(data_row)
|
|
190
195
|
|
|
191
|
-
data_row
|
|
192
|
-
|
|
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
|
|
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 -
|
|
212
|
+
new_y = @graph_top + (@graph_height - y_data * @graph_height)
|
|
209
213
|
|
|
210
214
|
# Reset each time to avoid thin-line errors
|
|
211
|
-
@
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 = [
|
|
239
|
-
possible_minimums = [
|
|
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
|
|
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
|
-
|
|
249
|
-
|
|
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
|
|
255
|
-
|
|
259
|
+
def normalize
|
|
260
|
+
return unless data_given?
|
|
256
261
|
|
|
257
|
-
@
|
|
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
|
-
|
|
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 -
|
|
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
|
|
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
|
-
|
|
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
|