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.
- 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 +112 -0
- data/.travis.yml +24 -15
- data/.yardopts +1 -0
- data/{History.txt → CHANGELOG.md} +72 -25
- data/Gemfile +3 -7
- data/README.md +57 -25
- data/Rakefile +21 -192
- 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 +21 -13
- 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 +435 -704
- data/lib/gruff/bezier.rb +32 -17
- data/lib/gruff/bullet.rb +62 -68
- data/lib/gruff/dot.rb +38 -82
- 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 +60 -0
- data/lib/gruff/line.rb +134 -170
- 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 +68 -81
- data/lib/gruff/patch/rmagick.rb +33 -0
- data/lib/gruff/patch/string.rb +10 -0
- data/lib/gruff/photo_bar.rb +39 -42
- data/lib/gruff/pie.rb +180 -89
- 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 +42 -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 +132 -0
- data/lib/gruff/renderer/text.rb +53 -0
- data/lib/gruff/scatter.rb +163 -182
- data/lib/gruff/scene.rb +31 -41
- data/lib/gruff/side_bar.rb +81 -65
- data/lib/gruff/side_stacked_bar.rb +78 -62
- data/lib/gruff/spider.rb +49 -57
- data/lib/gruff/stacked_area.rb +40 -32
- data/lib/gruff/stacked_bar.rb +86 -53
- data/lib/gruff/store/base_data.rb +38 -0
- data/lib/gruff/store/custom_data.rb +38 -0
- data/lib/gruff/store/store.rb +80 -0
- data/lib/gruff/store/xy_data.rb +59 -0
- data/lib/gruff/themes.rb +32 -33
- data/lib/gruff/version.rb +3 -1
- metadata +80 -102
- 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_labels_for_null_data.rb +0 -27
- 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 -161
- 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 -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
|
data/lib/gruff/line.rb
CHANGED
|
@@ -1,166 +1,165 @@
|
|
|
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
|
-
|
|
19
|
-
|
|
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
|
-
#
|
|
22
|
-
|
|
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
|
-
#
|
|
25
|
-
|
|
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
|
-
|
|
34
|
+
attr_writer :hide_dots, :hide_lines
|
|
30
35
|
|
|
31
|
-
#accessors for support of xy data
|
|
32
|
-
|
|
33
|
-
|
|
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
|
|
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] ||=
|
|
51
|
+
@reference_lines[:baseline] ||= {}
|
|
47
52
|
@reference_lines[:baseline][:value] = new_value
|
|
48
53
|
end
|
|
49
54
|
|
|
50
55
|
def baseline_color
|
|
51
|
-
if
|
|
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] ||=
|
|
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
|
|
66
|
+
# Call with target pixel width of graph (+800+, +400+, +300+), and/or +false+ to omit lines (points only).
|
|
64
67
|
#
|
|
65
|
-
#
|
|
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
|
-
#
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
92
|
-
# name
|
|
93
|
-
# x_data_points
|
|
94
|
-
# y_data_points
|
|
95
|
-
# color
|
|
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
|
-
#
|
|
100
|
-
#
|
|
101
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
118
|
-
#
|
|
119
|
-
#
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
|
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 = (
|
|
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
|
|
190
|
-
(0
|
|
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
|
-
|
|
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
|
-
|
|
205
|
-
|
|
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
|
-
|
|
200
|
+
store.norm_data.each do |data_row|
|
|
212
201
|
prev_x = prev_y = nil
|
|
213
202
|
|
|
214
|
-
|
|
203
|
+
one_point = contains_one_point_only?(data_row)
|
|
215
204
|
|
|
216
|
-
data_row
|
|
217
|
-
x_data
|
|
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
|
|
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
|
|
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 -
|
|
221
|
+
new_y = @graph_top + (@graph_height - y_data * @graph_height)
|
|
234
222
|
|
|
235
223
|
# Reset each time to avoid thin-line errors
|
|
236
|
-
@
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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
|
|
236
|
+
prev_x = new_x
|
|
237
|
+
prev_y = new_y
|
|
254
238
|
end
|
|
255
239
|
end
|
|
256
240
|
|
|
257
|
-
|
|
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 = [
|
|
264
|
-
possible_minimums = [
|
|
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
|
|
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
|
-
|
|
274
|
-
|
|
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
|
|
280
|
-
|
|
268
|
+
def normalize
|
|
269
|
+
return unless data_given?
|
|
281
270
|
|
|
282
|
-
@
|
|
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
|
-
|
|
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 -
|
|
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
|
|
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
|
-
|
|
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
|