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
data/lib/gruff/spider.rb
CHANGED
@@ -1,20 +1,26 @@
|
|
1
|
-
|
2
|
-
require File.dirname(__FILE__) + '/base'
|
1
|
+
# frozen_string_literal: true
|
3
2
|
|
4
3
|
# Experimental!!! See also the Net graph.
|
5
4
|
#
|
6
|
-
#
|
5
|
+
# Here's how to set up a Gruff::Spider.
|
6
|
+
#
|
7
|
+
# g = Gruff::Spider.new(30)
|
8
|
+
# g.title = "Spider Graph"
|
9
|
+
# g.data :Strength, [10]
|
10
|
+
# g.data :Dexterity, [16]
|
11
|
+
# g.data :Constitution, [12]
|
12
|
+
# g.data :Intelligence, [12]
|
13
|
+
# g.data :Wisdom, [10]
|
14
|
+
# g.data 'Charisma', [16]
|
15
|
+
# g.write("spider.png")
|
16
|
+
#
|
7
17
|
class Gruff::Spider < Gruff::Base
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
attr_reader :transparent_background
|
13
|
-
attr_accessor :rotation
|
14
|
-
|
18
|
+
# Hide all text.
|
19
|
+
attr_writer :hide_axes
|
20
|
+
attr_writer :rotation
|
21
|
+
|
15
22
|
def transparent_background=(value)
|
16
|
-
@
|
17
|
-
@base_image = render_transparent_background if value
|
23
|
+
Gruff::Renderer.setup_transparent_background(@columns, @rows) if value
|
18
24
|
end
|
19
25
|
|
20
26
|
def hide_text=(value)
|
@@ -24,16 +30,23 @@ class Gruff::Spider < Gruff::Base
|
|
24
30
|
def initialize(max_value, target_width = 800)
|
25
31
|
super(target_width)
|
26
32
|
@max_value = max_value
|
33
|
+
end
|
34
|
+
|
35
|
+
def initialize_ivars
|
36
|
+
super
|
27
37
|
@hide_legend = true
|
38
|
+
@hide_axes = false
|
39
|
+
@hide_text = false
|
28
40
|
@rotation = 0
|
29
41
|
end
|
30
|
-
|
42
|
+
private :initialize_ivars
|
43
|
+
|
31
44
|
def draw
|
32
45
|
@hide_line_markers = true
|
33
|
-
|
46
|
+
|
34
47
|
super
|
35
48
|
|
36
|
-
return unless
|
49
|
+
return unless data_given?
|
37
50
|
|
38
51
|
# Setup basic positioning
|
39
52
|
radius = @graph_height / 2.0
|
@@ -41,16 +54,14 @@ class Gruff::Spider < Gruff::Base
|
|
41
54
|
center_y = @graph_top + (@graph_height / 2.0) - 25 # Move graph up a bit
|
42
55
|
|
43
56
|
@unit_length = radius / @max_value
|
44
|
-
|
45
|
-
additive_angle = (2 * Math::PI)/
|
46
|
-
|
57
|
+
|
58
|
+
additive_angle = (2 * Math::PI) / store.length
|
59
|
+
|
47
60
|
# Draw axes
|
48
|
-
draw_axes(center_x, center_y, radius, additive_angle) unless hide_axes
|
61
|
+
draw_axes(center_x, center_y, radius, additive_angle) unless @hide_axes
|
49
62
|
|
50
63
|
# Draw polygon
|
51
64
|
draw_polygon(center_x, center_y, additive_angle)
|
52
|
-
|
53
|
-
@d.draw(@base_image)
|
54
65
|
end
|
55
66
|
|
56
67
|
private
|
@@ -60,42 +71,30 @@ private
|
|
60
71
|
end
|
61
72
|
|
62
73
|
def draw_label(center_x, center_y, angle, radius, amount)
|
63
|
-
r_offset = 50
|
74
|
+
r_offset = 50 # The distance out from the center of the pie to get point
|
64
75
|
x_offset = center_x # The label points need to be tweaked slightly
|
65
76
|
y_offset = center_y + 0 # This one doesn't though
|
66
77
|
x = x_offset + ((radius + r_offset) * Math.cos(angle))
|
67
78
|
y = y_offset + ((radius + r_offset) * Math.sin(angle))
|
68
79
|
|
69
80
|
# Draw label
|
70
|
-
|
71
|
-
|
72
|
-
@d.pointsize = scale_fontsize(legend_font_size)
|
73
|
-
@d.stroke = 'transparent'
|
74
|
-
@d.font_weight = BoldWeight
|
75
|
-
@d.gravity = CenterGravity
|
76
|
-
@d.annotate_scaled( @base_image,
|
77
|
-
0, 0,
|
78
|
-
x, y,
|
79
|
-
amount, @scale)
|
81
|
+
text_renderer = Gruff::Renderer::Text.new(amount, font: @font, size: @legend_font_size, color: @marker_color, weight: Magick::BoldWeight)
|
82
|
+
text_renderer.add_to_render_queue(0, 0, x, y, Magick::CenterGravity)
|
80
83
|
end
|
81
84
|
|
82
85
|
def draw_axes(center_x, center_y, radius, additive_angle, line_color = nil)
|
83
|
-
return if hide_axes
|
84
|
-
|
85
|
-
current_angle = rotation * Math::PI / 180.0
|
86
|
+
return if @hide_axes
|
86
87
|
|
87
|
-
@
|
88
|
-
@d.stroke(line_color || data_row[DATA_COLOR_INDEX])
|
89
|
-
@d.stroke_width 5.0
|
88
|
+
current_angle = @rotation * Math::PI / 180.0
|
90
89
|
|
90
|
+
store.data.each do |data_row|
|
91
91
|
x_offset = radius * Math.cos(current_angle)
|
92
92
|
y_offset = radius * Math.sin(current_angle)
|
93
93
|
|
94
|
-
|
95
|
-
|
96
|
-
center_y + y_offset)
|
94
|
+
Gruff::Renderer::Line.new(color: line_color || data_row.color, width: 5.0)
|
95
|
+
.render(center_x, center_y, center_x + x_offset, center_y + y_offset)
|
97
96
|
|
98
|
-
draw_label(center_x, center_y, current_angle, radius, data_row
|
97
|
+
draw_label(center_x, center_y, current_angle, radius, data_row.label.to_s) unless @hide_text
|
99
98
|
|
100
99
|
current_angle += additive_angle
|
101
100
|
end
|
@@ -103,23 +102,18 @@ private
|
|
103
102
|
|
104
103
|
def draw_polygon(center_x, center_y, additive_angle, color = nil)
|
105
104
|
points = []
|
106
|
-
current_angle = rotation * Math::PI / 180.0
|
105
|
+
current_angle = @rotation * Math::PI / 180.0
|
107
106
|
|
108
|
-
|
109
|
-
points << center_x + normalize_points(data_row
|
110
|
-
points << center_y + normalize_points(data_row
|
107
|
+
store.data.each do |data_row|
|
108
|
+
points << center_x + normalize_points(data_row.points.first) * Math.cos(current_angle)
|
109
|
+
points << center_y + normalize_points(data_row.points.first) * Math.sin(current_angle)
|
111
110
|
current_angle += additive_angle
|
112
111
|
end
|
113
112
|
|
114
|
-
|
115
|
-
@d.stroke(color || @marker_color)
|
116
|
-
@d.fill(color || @marker_color)
|
117
|
-
@d.fill_opacity 0.4
|
118
|
-
@d.polygon(*points)
|
113
|
+
Gruff::Renderer::Polygon.new(color: color || @marker_color, opacity: 0.4).render(points)
|
119
114
|
end
|
120
115
|
|
121
116
|
def sums_for_spider
|
122
|
-
|
117
|
+
store.data.sum { |data_row| data_row.points.first }
|
123
118
|
end
|
124
|
-
|
125
119
|
end
|
data/lib/gruff/stacked_area.rb
CHANGED
@@ -1,67 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
|
-
|
3
|
-
|
4
|
-
|
3
|
+
#
|
4
|
+
# Here's how to set up a Gruff::StackedArea.
|
5
|
+
#
|
6
|
+
# g = Gruff::StackedArea.new
|
7
|
+
# g.title = 'StackedArea Graph'
|
8
|
+
# g.data :Jimmy, [25, 36, 86, 39, 25, 31, 79, 88]
|
9
|
+
# g.data :Charles, [80, 54, 67, 54, 68, 70, 90, 95]
|
10
|
+
# g.data :Julie, [22, 29, 35, 38, 36, 40, 46, 57]
|
11
|
+
# g.write('stacked_area.png')
|
12
|
+
#
|
5
13
|
class Gruff::StackedArea < Gruff::Base
|
6
14
|
include StackedMixin
|
7
|
-
|
8
|
-
|
15
|
+
attr_writer :last_series_goes_on_bottom
|
16
|
+
|
17
|
+
def initialize_ivars
|
18
|
+
super
|
19
|
+
@last_series_goes_on_bottom = false
|
20
|
+
end
|
21
|
+
private :initialize_ivars
|
22
|
+
|
9
23
|
def draw
|
10
|
-
|
24
|
+
calculate_maximum_by_stack
|
11
25
|
super
|
12
26
|
|
13
|
-
return unless
|
27
|
+
return unless data_given?
|
14
28
|
|
15
|
-
|
16
|
-
@d = @d.stroke 'transparent'
|
29
|
+
x_increment = @graph_width / (column_count - 1).to_f
|
17
30
|
|
18
|
-
height = Array.new(
|
31
|
+
height = Array.new(column_count, 0)
|
19
32
|
|
20
33
|
data_points = nil
|
21
|
-
iterator = last_series_goes_on_bottom ? :reverse_each : :each
|
22
|
-
|
34
|
+
iterator = @last_series_goes_on_bottom ? :reverse_each : :each
|
35
|
+
store.norm_data.public_send(iterator) do |data_row|
|
23
36
|
prev_data_points = data_points
|
24
|
-
data_points =
|
25
|
-
|
26
|
-
@d = @d.fill data_row[DATA_COLOR_INDEX]
|
37
|
+
data_points = []
|
27
38
|
|
28
|
-
data_row
|
39
|
+
data_row.points.each_with_index do |data_point, index|
|
29
40
|
# Use incremented x and scaled y
|
30
|
-
new_x = @graph_left + (
|
41
|
+
new_x = @graph_left + (x_increment * index)
|
31
42
|
new_y = @graph_top + (@graph_height - data_point * @graph_height - height[index])
|
32
43
|
|
33
44
|
height[index] += (data_point * @graph_height)
|
34
|
-
|
45
|
+
|
35
46
|
data_points << new_x
|
36
47
|
data_points << new_y
|
37
|
-
|
38
|
-
draw_label(new_x, index)
|
39
48
|
|
49
|
+
draw_label(new_x, index)
|
40
50
|
end
|
41
51
|
|
52
|
+
poly_points = data_points.dup
|
42
53
|
if prev_data_points
|
43
|
-
|
44
|
-
|
45
|
-
poly_points << prev_data_points[2*i]
|
46
|
-
poly_points << prev_data_points[2*i+1]
|
54
|
+
(prev_data_points.length / 2 - 1).downto(0) do |i|
|
55
|
+
poly_points << prev_data_points[2 * i]
|
56
|
+
poly_points << prev_data_points[2 * i + 1]
|
47
57
|
end
|
48
|
-
poly_points << data_points[0]
|
49
|
-
poly_points << data_points[1]
|
50
58
|
else
|
51
|
-
poly_points = data_points.dup
|
52
59
|
poly_points << @graph_right
|
53
60
|
poly_points << @graph_bottom - 1
|
54
61
|
poly_points << @graph_left
|
55
62
|
poly_points << @graph_bottom - 1
|
56
|
-
poly_points << data_points[0]
|
57
|
-
poly_points << data_points[1]
|
58
63
|
end
|
59
|
-
|
64
|
+
poly_points << data_points[0]
|
65
|
+
poly_points << data_points[1]
|
60
66
|
|
67
|
+
Gruff::Renderer::Polygon.new(color: data_row.color).render(poly_points)
|
61
68
|
end
|
62
|
-
|
63
|
-
@d.draw(@base_image)
|
64
69
|
end
|
65
|
-
|
66
|
-
|
67
70
|
end
|
data/lib/gruff/stacked_bar.rb
CHANGED
@@ -1,61 +1,106 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
|
-
|
3
|
-
|
4
|
-
|
3
|
+
#
|
4
|
+
# Here's how to set up a Gruff::StackedBar.
|
5
|
+
#
|
6
|
+
# g = Gruff::StackedBar.new
|
7
|
+
# g.title = 'StackedBar Graph'
|
8
|
+
# g.data :Art, [0, 5, 8, 15]
|
9
|
+
# g.data :Philosophy, [10, 3, 2, 8]
|
10
|
+
# g.data :Science, [2, 15, 8, 11]
|
11
|
+
# g.write('stacked_bar.png')
|
12
|
+
#
|
5
13
|
class Gruff::StackedBar < Gruff::Base
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
end
|
14
|
+
include StackedMixin
|
15
|
+
include BarValueLabelMixin
|
16
|
+
|
17
|
+
# Spacing factor applied between bars.
|
18
|
+
attr_writer :bar_spacing
|
19
|
+
|
20
|
+
# Number of pixels between bar segments.
|
21
|
+
attr_writer :segment_spacing
|
22
|
+
|
23
|
+
# Set the number output format for labels using sprintf.
|
24
|
+
# Default is +"%.2f"+.
|
25
|
+
attr_writer :label_formatting
|
26
|
+
|
27
|
+
# Output the values for the bars on a bar graph.
|
28
|
+
# Default is +false+.
|
29
|
+
attr_writer :show_labels_for_bar_values
|
30
|
+
|
31
|
+
# Prevent drawing of column labels below a stacked bar graph. Default is +false+.
|
32
|
+
attr_writer :hide_labels
|
33
|
+
|
34
|
+
def initialize_ivars
|
35
|
+
super
|
36
|
+
@bar_spacing = 0.9
|
37
|
+
@segment_spacing = 2
|
38
|
+
@label_formatting = nil
|
39
|
+
@show_labels_for_bar_values = false
|
40
|
+
@hide_labels = false
|
41
|
+
end
|
42
|
+
private :initialize_ivars
|
43
|
+
|
44
|
+
# Draws a bar graph, but multiple sets are stacked on top of each other.
|
45
|
+
def draw
|
46
|
+
calculate_maximum_by_stack
|
47
|
+
super
|
48
|
+
return unless data_given?
|
49
|
+
|
50
|
+
# Setup spacing.
|
51
|
+
#
|
52
|
+
# Columns sit stacked.
|
53
|
+
bar_width = @graph_width / column_count.to_f
|
54
|
+
padding = (bar_width * (1 - @bar_spacing)) / 2
|
55
|
+
|
56
|
+
height = Array.new(column_count, 0)
|
57
|
+
bar_value_label = BarValueLabel.new(column_count, bar_width)
|
58
|
+
|
59
|
+
store.norm_data.each_with_index do |data_row, row_index|
|
60
|
+
data_row.points.each_with_index do |data_point, point_index|
|
61
|
+
next if data_point == 0
|
55
62
|
|
63
|
+
# Use incremented x and scaled y
|
64
|
+
left_x = @graph_left + (bar_width * point_index) + padding
|
65
|
+
left_y = @graph_top + (@graph_height -
|
66
|
+
data_point * @graph_height -
|
67
|
+
height[point_index]) + @segment_spacing
|
68
|
+
right_x = left_x + bar_width * @bar_spacing
|
69
|
+
right_y = @graph_top + @graph_height - height[point_index]
|
70
|
+
|
71
|
+
# update the total height of the current stacked bar
|
72
|
+
height[point_index] += (data_point * @graph_height)
|
73
|
+
|
74
|
+
rect_renderer = Gruff::Renderer::Rectangle.new(color: data_row.color)
|
75
|
+
rect_renderer.render(left_x, left_y, right_x, right_y)
|
76
|
+
|
77
|
+
# Calculate center based on bar_width and current row
|
78
|
+
label_center = left_x + bar_width * @bar_spacing / 2.0
|
79
|
+
draw_label(label_center, point_index)
|
80
|
+
|
81
|
+
bar_value_label.coordinates[point_index] = [left_x, left_y, right_x, right_y]
|
82
|
+
bar_value_label.values[point_index] += store.data[row_index].points[point_index]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
if @show_labels_for_bar_values
|
87
|
+
bar_value_label.prepare_rendering(@label_formatting) do |x, y, text|
|
88
|
+
draw_value_label(x, y, text, true)
|
56
89
|
end
|
57
|
-
|
58
|
-
@d.draw(@base_image)
|
59
90
|
end
|
91
|
+
end
|
92
|
+
|
93
|
+
protected
|
94
|
+
|
95
|
+
def hide_labels?
|
96
|
+
@hide_labels
|
97
|
+
end
|
98
|
+
|
99
|
+
def hide_left_label_area?
|
100
|
+
@hide_line_markers
|
101
|
+
end
|
60
102
|
|
103
|
+
def hide_bottom_label_area?
|
104
|
+
hide_labels?
|
105
|
+
end
|
61
106
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gruff
|
4
|
+
class Store
|
5
|
+
# @private
|
6
|
+
class BasicData < Struct.new(:label, :points, :color)
|
7
|
+
def initialize(label, points, color)
|
8
|
+
super(label.to_s, Array(points), color)
|
9
|
+
end
|
10
|
+
|
11
|
+
def empty?
|
12
|
+
points.empty?
|
13
|
+
end
|
14
|
+
|
15
|
+
def columns
|
16
|
+
points.length
|
17
|
+
end
|
18
|
+
|
19
|
+
def min
|
20
|
+
points.compact.min
|
21
|
+
end
|
22
|
+
|
23
|
+
def max
|
24
|
+
points.compact.max
|
25
|
+
end
|
26
|
+
|
27
|
+
def normalize(minimum:, spread:)
|
28
|
+
norm_points = points.map do |point|
|
29
|
+
point.nil? ? nil : (point.to_f - minimum.to_f) / spread
|
30
|
+
end
|
31
|
+
|
32
|
+
self.class.new(label, norm_points, color)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|