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.
Files changed (120) hide show
  1. checksums.yaml +5 -5
  2. data/.editorconfig +14 -0
  3. data/.github/ISSUE_TEMPLATE.md +18 -0
  4. data/.gitignore +3 -0
  5. data/.rubocop.yml +109 -0
  6. data/.rubocop_todo.yml +130 -0
  7. data/.travis.yml +24 -12
  8. data/.yardopts +1 -0
  9. data/{History.txt → CHANGELOG.md} +61 -25
  10. data/Gemfile +3 -7
  11. data/README.md +57 -25
  12. data/Rakefile +6 -201
  13. data/assets/plastik/blue.png +0 -0
  14. data/assets/plastik/green.png +0 -0
  15. data/assets/plastik/red.png +0 -0
  16. data/docker/Dockerfile +14 -0
  17. data/docker/build.sh +4 -0
  18. data/docker/launch.sh +4 -0
  19. data/gruff.gemspec +19 -14
  20. data/init.rb +2 -0
  21. data/lib/gruff.rb +26 -2
  22. data/lib/gruff/accumulator_bar.rb +18 -8
  23. data/lib/gruff/area.rb +33 -19
  24. data/lib/gruff/bar.rb +76 -45
  25. data/lib/gruff/base.rb +337 -613
  26. data/lib/gruff/bezier.rb +34 -19
  27. data/lib/gruff/bullet.rb +51 -62
  28. data/lib/gruff/dot.rb +38 -62
  29. data/lib/gruff/helper/bar_conversion.rb +47 -0
  30. data/lib/gruff/helper/bar_value_label_mixin.rb +30 -0
  31. data/lib/gruff/helper/stacked_mixin.rb +23 -0
  32. data/lib/gruff/histogram.rb +59 -0
  33. data/lib/gruff/line.rb +130 -150
  34. data/lib/gruff/mini/bar.rb +17 -10
  35. data/lib/gruff/mini/legend.rb +24 -36
  36. data/lib/gruff/mini/pie.rb +18 -12
  37. data/lib/gruff/mini/side_bar.rb +26 -12
  38. data/lib/gruff/net.rb +60 -84
  39. data/lib/gruff/patch/rmagick.rb +33 -0
  40. data/lib/gruff/patch/string.rb +10 -0
  41. data/lib/gruff/photo_bar.rb +27 -30
  42. data/lib/gruff/pie.rb +190 -93
  43. data/lib/gruff/renderer/bezier.rb +21 -0
  44. data/lib/gruff/renderer/circle.rb +21 -0
  45. data/lib/gruff/renderer/dash_line.rb +22 -0
  46. data/lib/gruff/renderer/dot.rb +39 -0
  47. data/lib/gruff/renderer/ellipse.rb +21 -0
  48. data/lib/gruff/renderer/line.rb +34 -0
  49. data/lib/gruff/renderer/polygon.rb +23 -0
  50. data/lib/gruff/renderer/polyline.rb +21 -0
  51. data/lib/gruff/renderer/rectangle.rb +19 -0
  52. data/lib/gruff/renderer/renderer.rb +127 -0
  53. data/lib/gruff/renderer/text.rb +42 -0
  54. data/lib/gruff/scatter.rb +156 -180
  55. data/lib/gruff/scene.rb +31 -41
  56. data/lib/gruff/side_bar.rb +77 -63
  57. data/lib/gruff/side_stacked_bar.rb +77 -60
  58. data/lib/gruff/spider.rb +37 -50
  59. data/lib/gruff/stacked_area.rb +32 -30
  60. data/lib/gruff/stacked_bar.rb +87 -49
  61. data/lib/gruff/store/base_data.rb +34 -0
  62. data/lib/gruff/store/custom_data.rb +34 -0
  63. data/lib/gruff/store/store.rb +80 -0
  64. data/lib/gruff/store/xy_data.rb +55 -0
  65. data/lib/gruff/themes.rb +32 -33
  66. data/lib/gruff/version.rb +3 -1
  67. metadata +88 -94
  68. data/Manifest.txt +0 -81
  69. data/RELEASE.md +0 -30
  70. data/assets/bubble.png +0 -0
  71. data/assets/city_scene/background/0000.png +0 -0
  72. data/assets/city_scene/background/0600.png +0 -0
  73. data/assets/city_scene/background/2000.png +0 -0
  74. data/assets/city_scene/clouds/cloudy.png +0 -0
  75. data/assets/city_scene/clouds/partly_cloudy.png +0 -0
  76. data/assets/city_scene/clouds/stormy.png +0 -0
  77. data/assets/city_scene/grass/default.png +0 -0
  78. data/assets/city_scene/haze/true.png +0 -0
  79. data/assets/city_scene/number_sample/1.png +0 -0
  80. data/assets/city_scene/number_sample/2.png +0 -0
  81. data/assets/city_scene/number_sample/default.png +0 -0
  82. data/assets/city_scene/sky/0000.png +0 -0
  83. data/assets/city_scene/sky/0200.png +0 -0
  84. data/assets/city_scene/sky/0400.png +0 -0
  85. data/assets/city_scene/sky/0600.png +0 -0
  86. data/assets/city_scene/sky/0800.png +0 -0
  87. data/assets/city_scene/sky/1000.png +0 -0
  88. data/assets/city_scene/sky/1200.png +0 -0
  89. data/assets/city_scene/sky/1400.png +0 -0
  90. data/assets/city_scene/sky/1500.png +0 -0
  91. data/assets/city_scene/sky/1700.png +0 -0
  92. data/assets/city_scene/sky/2000.png +0 -0
  93. data/assets/pc306715.jpg +0 -0
  94. data/lib/gruff/bar_conversion.rb +0 -46
  95. data/lib/gruff/deprecated.rb +0 -39
  96. data/lib/gruff/stacked_mixin.rb +0 -23
  97. data/test/gruff_test_case.rb +0 -154
  98. data/test/image_compare.rb +0 -58
  99. data/test/test_accumulator_bar.rb +0 -51
  100. data/test/test_area.rb +0 -134
  101. data/test/test_bar.rb +0 -505
  102. data/test/test_base.rb +0 -8
  103. data/test/test_bezier.rb +0 -33
  104. data/test/test_bullet.rb +0 -26
  105. data/test/test_dot.rb +0 -263
  106. data/test/test_legend.rb +0 -68
  107. data/test/test_line.rb +0 -657
  108. data/test/test_mini_bar.rb +0 -33
  109. data/test/test_mini_pie.rb +0 -25
  110. data/test/test_mini_side_bar.rb +0 -36
  111. data/test/test_net.rb +0 -231
  112. data/test/test_photo.rb +0 -41
  113. data/test/test_pie.rb +0 -154
  114. data/test/test_scatter.rb +0 -233
  115. data/test/test_scene.rb +0 -100
  116. data/test/test_side_bar.rb +0 -56
  117. data/test/test_sidestacked_bar.rb +0 -105
  118. data/test/test_spider.rb +0 -226
  119. data/test/test_stacked_area.rb +0 -52
  120. data/test/test_stacked_bar.rb +0 -52
@@ -1,20 +1,30 @@
1
+ # frozen_string_literal: true
1
2
 
2
- require File.dirname(__FILE__) + '/base'
3
+ require 'gruff/base'
3
4
 
4
5
  # Experimental!!! See also the Net graph.
5
6
  #
6
- # Submitted by Kevin Clark http://glu.ttono.us/
7
+ # Here's how to set up a Gruff::Spider.
8
+ #
9
+ # g = Gruff::Spider.new(30)
10
+ # g.title = "Spider Graph"
11
+ # g.data :Strength, [10]
12
+ # g.data :Dexterity, [16]
13
+ # g.data :Constitution, [12]
14
+ # g.data :Intelligence, [12]
15
+ # g.data :Wisdom, [10]
16
+ # g.data 'Charisma', [16]
17
+ # g.write("spider.png")
18
+
7
19
  class Gruff::Spider < Gruff::Base
8
-
9
- # Hide all text
20
+ # Hide all text.
10
21
  attr_reader :hide_text
11
22
  attr_accessor :hide_axes
12
23
  attr_reader :transparent_background
13
24
  attr_accessor :rotation
14
-
25
+
15
26
  def transparent_background=(value)
16
- @transparent_background = value
17
- @base_image = render_transparent_background if value
27
+ Gruff::Renderer.setup_transparent_background(@columns, @rows) if value
18
28
  end
19
29
 
20
30
  def hide_text=(value)
@@ -24,39 +34,33 @@ class Gruff::Spider < Gruff::Base
24
34
  def initialize(max_value, target_width = 800)
25
35
  super(target_width)
26
36
  @max_value = max_value
27
- @hide_legend = true;
37
+ @hide_legend = true
28
38
  @rotation = 0
29
39
  end
30
-
40
+
31
41
  def draw
32
42
  @hide_line_markers = true
33
-
43
+
34
44
  super
35
45
 
36
- return unless @has_data
46
+ return unless data_given?
37
47
 
38
48
  # Setup basic positioning
39
- diameter = @graph_height
40
49
  radius = @graph_height / 2.0
41
- top_x = @graph_left + (@graph_width - diameter) / 2.0
42
50
  center_x = @graph_left + (@graph_width / 2.0)
43
51
  center_y = @graph_top + (@graph_height / 2.0) - 25 # Move graph up a bit
44
52
 
45
53
  @unit_length = radius / @max_value
46
-
47
- total_sum = sums_for_spider
48
- prev_degrees = 0.0
49
- additive_angle = (2 * Math::PI)/ @data.size
50
-
51
- current_angle = rotation * Math::PI / 180.0
54
+
55
+ additive_angle = (2 * Math::PI) / store.length
52
56
 
53
57
  # Draw axes
54
- draw_axes(center_x, center_y, radius, additive_angle) unless hide_axes
58
+ draw_axes(center_x, center_y, radius, additive_angle) unless hide_axes
55
59
 
56
60
  # Draw polygon
57
61
  draw_polygon(center_x, center_y, additive_angle)
58
62
 
59
- @d.draw(@base_image)
63
+ Gruff::Renderer.finish
60
64
  end
61
65
 
62
66
  private
@@ -66,23 +70,15 @@ private
66
70
  end
67
71
 
68
72
  def draw_label(center_x, center_y, angle, radius, amount)
69
- r_offset = 50 # The distance out from the center of the pie to get point
73
+ r_offset = 50 # The distance out from the center of the pie to get point
70
74
  x_offset = center_x # The label points need to be tweaked slightly
71
75
  y_offset = center_y + 0 # This one doesn't though
72
76
  x = x_offset + ((radius + r_offset) * Math.cos(angle))
73
77
  y = y_offset + ((radius + r_offset) * Math.sin(angle))
74
78
 
75
79
  # Draw label
76
- @d.fill = @marker_color
77
- @d.font = @font if @font
78
- @d.pointsize = scale_fontsize(legend_font_size)
79
- @d.stroke = 'transparent'
80
- @d.font_weight = BoldWeight
81
- @d.gravity = CenterGravity
82
- @d.annotate_scaled( @base_image,
83
- 0, 0,
84
- x, y,
85
- amount, @scale)
80
+ text_renderer = Gruff::Renderer::Text.new(amount, font: @font, size: legend_font_size, color: @marker_color, weight: Magick::BoldWeight)
81
+ text_renderer.render(0, 0, x, y, Magick::CenterGravity)
86
82
  end
87
83
 
88
84
  def draw_axes(center_x, center_y, radius, additive_angle, line_color = nil)
@@ -90,18 +86,14 @@ private
90
86
 
91
87
  current_angle = rotation * Math::PI / 180.0
92
88
 
93
- @data.each do |data_row|
94
- @d.stroke(line_color || data_row[DATA_COLOR_INDEX])
95
- @d.stroke_width 5.0
96
-
89
+ store.data.each do |data_row|
97
90
  x_offset = radius * Math.cos(current_angle)
98
91
  y_offset = radius * Math.sin(current_angle)
99
92
 
100
- @d.line(center_x, center_y,
101
- center_x + x_offset,
102
- center_y + y_offset)
93
+ Gruff::Renderer::Line.new(color: line_color || data_row.color, width: 5.0)
94
+ .render(center_x, center_y, center_x + x_offset, center_y + y_offset)
103
95
 
104
- draw_label(center_x, center_y, current_angle, radius, data_row[DATA_LABEL_INDEX].to_s) unless hide_text
96
+ draw_label(center_x, center_y, current_angle, radius, data_row.label.to_s) unless hide_text
105
97
 
106
98
  current_angle += additive_angle
107
99
  end
@@ -111,21 +103,16 @@ private
111
103
  points = []
112
104
  current_angle = rotation * Math::PI / 180.0
113
105
 
114
- @data.each do |data_row|
115
- points << center_x + normalize_points(data_row[DATA_VALUES_INDEX].first) * Math.cos(current_angle)
116
- points << center_y + normalize_points(data_row[DATA_VALUES_INDEX].first) * Math.sin(current_angle)
106
+ store.data.each do |data_row|
107
+ points << center_x + normalize_points(data_row.points.first) * Math.cos(current_angle)
108
+ points << center_y + normalize_points(data_row.points.first) * Math.sin(current_angle)
117
109
  current_angle += additive_angle
118
110
  end
119
111
 
120
- @d.stroke_width 1.0
121
- @d.stroke(color || @marker_color)
122
- @d.fill(color || @marker_color)
123
- @d.fill_opacity 0.4
124
- @d.polygon(*points)
112
+ Gruff::Renderer::Polygon.new(color: color || @marker_color, opacity: 0.4).render(points)
125
113
  end
126
114
 
127
115
  def sums_for_spider
128
- @data.inject(0.0) {|sum, data_row| sum += data_row[DATA_VALUES_INDEX].first}
116
+ store.data.reduce(0.0) { |sum, data_row| sum + data_row.points.first }
129
117
  end
130
-
131
118
  end
@@ -1,67 +1,69 @@
1
+ # frozen_string_literal: true
1
2
 
2
- require File.dirname(__FILE__) + '/base'
3
- require File.dirname(__FILE__) + '/stacked_mixin'
3
+ require 'gruff/base'
4
+ require 'gruff/helper/stacked_mixin'
4
5
 
6
+ #
7
+ # Here's how to set up a Gruff::StackedArea.
8
+ #
9
+ # g = Gruff::StackedArea.new
10
+ # g.title = 'StackedArea Graph'
11
+ # g.data :Jimmy, [25, 36, 86, 39, 25, 31, 79, 88]
12
+ # g.data :Charles, [80, 54, 67, 54, 68, 70, 90, 95]
13
+ # g.data :Julie, [22, 29, 35, 38, 36, 40, 46, 57]
14
+ # g.write('stacked_area.png')
15
+ #
5
16
  class Gruff::StackedArea < Gruff::Base
6
17
  include StackedMixin
7
18
  attr_accessor :last_series_goes_on_bottom
8
-
19
+
9
20
  def draw
10
- get_maximum_by_stack
21
+ calculate_maximum_by_stack
11
22
  super
12
23
 
13
- return unless @has_data
24
+ return unless data_given?
14
25
 
15
- @x_increment = @graph_width / (@column_count - 1).to_f
16
- @d = @d.stroke 'transparent'
26
+ x_increment = @graph_width / (column_count - 1).to_f
17
27
 
18
- height = Array.new(@column_count, 0)
28
+ height = Array.new(column_count, 0)
19
29
 
20
30
  data_points = nil
21
31
  iterator = last_series_goes_on_bottom ? :reverse_each : :each
22
- @norm_data.send(iterator) do |data_row|
32
+ store.norm_data.public_send(iterator) do |data_row|
23
33
  prev_data_points = data_points
24
- data_points = Array.new
25
-
26
- @d = @d.fill data_row[DATA_COLOR_INDEX]
34
+ data_points = []
27
35
 
28
- data_row[DATA_VALUES_INDEX].each_with_index do |data_point, index|
36
+ data_row.points.each_with_index do |data_point, index|
29
37
  # Use incremented x and scaled y
30
- new_x = @graph_left + (@x_increment * index)
38
+ new_x = @graph_left + (x_increment * index)
31
39
  new_y = @graph_top + (@graph_height - data_point * @graph_height - height[index])
32
40
 
33
41
  height[index] += (data_point * @graph_height)
34
-
42
+
35
43
  data_points << new_x
36
44
  data_points << new_y
37
-
38
- draw_label(new_x, index)
39
45
 
46
+ draw_label(new_x, index)
40
47
  end
41
48
 
49
+ poly_points = data_points.dup
42
50
  if prev_data_points
43
- poly_points = data_points.dup
44
- (prev_data_points.length/2 - 1).downto(0) do |i|
45
- poly_points << prev_data_points[2*i]
46
- poly_points << prev_data_points[2*i+1]
51
+ (prev_data_points.length / 2 - 1).downto(0) do |i|
52
+ poly_points << prev_data_points[2 * i]
53
+ poly_points << prev_data_points[2 * i + 1]
47
54
  end
48
- poly_points << data_points[0]
49
- poly_points << data_points[1]
50
55
  else
51
- poly_points = data_points.dup
52
56
  poly_points << @graph_right
53
57
  poly_points << @graph_bottom - 1
54
58
  poly_points << @graph_left
55
59
  poly_points << @graph_bottom - 1
56
- poly_points << data_points[0]
57
- poly_points << data_points[1]
58
60
  end
59
- @d = @d.polyline(*poly_points)
61
+ poly_points << data_points[0]
62
+ poly_points << data_points[1]
60
63
 
64
+ Gruff::Renderer::Polygon.new(color: data_row.color).render(poly_points)
61
65
  end
62
66
 
63
- @d.draw(@base_image)
67
+ Gruff::Renderer.finish
64
68
  end
65
-
66
-
67
69
  end
@@ -1,57 +1,95 @@
1
+ # frozen_string_literal: true
1
2
 
2
- require File.dirname(__FILE__) + '/base'
3
- require File.dirname(__FILE__) + '/stacked_mixin'
3
+ require 'gruff/base'
4
+ require 'gruff/helper/stacked_mixin'
5
+ require 'gruff/helper/bar_value_label_mixin'
4
6
 
7
+ #
8
+ # Here's how to set up a Gruff::StackedBar.
9
+ #
10
+ # g = Gruff::StackedBar.new
11
+ # g.title = 'StackedBar Graph'
12
+ # g.data :Art, [0, 5, 8, 15]
13
+ # g.data :Philosophy, [10, 3, 2, 8]
14
+ # g.data :Science, [2, 15, 8, 11]
15
+ # g.write('stacked_bar.png')
16
+ #
5
17
  class Gruff::StackedBar < Gruff::Base
6
- include StackedMixin
7
-
8
- # Spacing factor applied between bars
9
- attr_accessor :bar_spacing
10
-
11
- # Draws a bar graph, but multiple sets are stacked on top of each other.
12
- def draw
13
- get_maximum_by_stack
14
- super
15
- return unless @has_data
16
-
17
- # Setup spacing.
18
- #
19
- # Columns sit stacked.
20
- @bar_spacing ||= 0.9
21
- @bar_width = @graph_width / @column_count.to_f
22
- padding = (@bar_width * (1 - @bar_spacing)) / 2
23
-
24
- @d = @d.stroke_opacity 0.0
25
-
26
- height = Array.new(@column_count, 0)
27
-
28
- @norm_data.each_with_index do |data_row, row_index|
29
- data_row[DATA_VALUES_INDEX].each_with_index do |data_point, point_index|
30
- @d = @d.fill data_row[DATA_COLOR_INDEX]
31
-
32
- # Calculate center based on bar_width and current row
33
- label_center = @graph_left + (@bar_width * point_index) + (@bar_width * @bar_spacing / 2.0)
34
- draw_label(label_center, point_index)
35
-
36
- next if (data_point == 0)
37
- # Use incremented x and scaled y
38
- left_x = @graph_left + (@bar_width * point_index) + padding
39
- left_y = @graph_top + (@graph_height -
40
- data_point * @graph_height -
41
- height[point_index]) + 1
42
- right_x = left_x + @bar_width * @bar_spacing
43
- right_y = @graph_top + @graph_height - height[point_index] - 1
44
-
45
- # update the total height of the current stacked bar
46
- height[point_index] += (data_point * @graph_height )
47
-
48
- @d = @d.rectangle(left_x, left_y, right_x, right_y)
49
-
50
- end
18
+ include StackedMixin
19
+ include BarValueLabelMixin
51
20
 
21
+ # Spacing factor applied between bars.
22
+ attr_accessor :bar_spacing
23
+
24
+ # Number of pixels between bar segments.
25
+ attr_accessor :segment_spacing
26
+
27
+ # Set the number output format for labels using sprintf.
28
+ # Default is +"%.2f"+.
29
+ attr_accessor :label_formatting
30
+
31
+ # Output the values for the bars on a bar graph.
32
+ # Default is +false+.
33
+ attr_accessor :show_labels_for_bar_values
34
+
35
+ def initialize_ivars
36
+ super
37
+ @label_formatting = nil
38
+ @show_labels_for_bar_values = false
39
+ end
40
+ private :initialize_ivars
41
+
42
+ # Draws a bar graph, but multiple sets are stacked on top of each other.
43
+ def draw
44
+ calculate_maximum_by_stack
45
+ super
46
+ return unless data_given?
47
+
48
+ # Setup spacing.
49
+ #
50
+ # Columns sit stacked.
51
+ @bar_spacing ||= 0.9
52
+ @segment_spacing ||= 2
53
+
54
+ bar_width = @graph_width / column_count.to_f
55
+ padding = (bar_width * (1 - @bar_spacing)) / 2
56
+
57
+ height = Array.new(column_count, 0)
58
+ bar_value_label = BarValueLabel.new(column_count, bar_width)
59
+
60
+ store.norm_data.each_with_index do |data_row, row_index|
61
+ data_row.points.each_with_index do |data_point, point_index|
62
+ next if data_point == 0
63
+
64
+ # Use incremented x and scaled y
65
+ left_x = @graph_left + (bar_width * point_index) + padding
66
+ left_y = @graph_top + (@graph_height -
67
+ data_point * @graph_height -
68
+ height[point_index]) + @segment_spacing
69
+ right_x = left_x + bar_width * @bar_spacing
70
+ right_y = @graph_top + @graph_height - height[point_index]
71
+
72
+ # update the total height of the current stacked bar
73
+ height[point_index] += (data_point * @graph_height)
74
+
75
+ rect_renderer = Gruff::Renderer::Rectangle.new(color: data_row.color)
76
+ rect_renderer.render(left_x, left_y, right_x, right_y)
77
+
78
+ # Calculate center based on bar_width and current row
79
+ label_center = left_x + bar_width * @bar_spacing / 2.0
80
+ draw_label(label_center, point_index)
81
+
82
+ bar_value_label.coordinates[point_index] = [left_x, left_y, right_x, right_y]
83
+ bar_value_label.values[point_index] += store.data[row_index].points[point_index]
84
+ end
85
+ end
86
+
87
+ if @show_labels_for_bar_values
88
+ bar_value_label.prepare_rendering(@label_formatting) do |x, y, text|
89
+ draw_value_label(x, y, text, true)
52
90
  end
53
-
54
- @d.draw(@base_image)
55
91
  end
56
92
 
93
+ Gruff::Renderer.finish
94
+ end
57
95
  end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gruff
4
+ # @private
5
+ class Store
6
+ class BaseData < Struct.new(:label, :points, :color)
7
+ def initialize(label, points, color)
8
+ self.label = label.to_s
9
+ self.points = Array(points)
10
+ self.color = color
11
+ end
12
+
13
+ def columns
14
+ points.length
15
+ end
16
+
17
+ def min
18
+ points.compact.min
19
+ end
20
+
21
+ def max
22
+ points.compact.max
23
+ end
24
+
25
+ def normalize(args = {})
26
+ norm_points = points.map do |point|
27
+ point.nil? ? nil : (point.to_f - args[:minimum].to_f) / args[:spread]
28
+ end
29
+
30
+ self.class.new(label, norm_points, color)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gruff
4
+ class Store
5
+ class CustomData < Struct.new(:label, :points, :color, :custom)
6
+ def initialize(label, points, color, custom = nil)
7
+ self.label = label.to_s
8
+ self.points = Array(points)
9
+ self.color = color
10
+ self.custom = custom
11
+ end
12
+
13
+ def columns
14
+ points.length
15
+ end
16
+
17
+ def min
18
+ points.compact.min
19
+ end
20
+
21
+ def max
22
+ points.compact.max
23
+ end
24
+
25
+ def normalize(args = {})
26
+ norm_points = points.map do |point|
27
+ point.nil? ? nil : (point.to_f - args[:minimum].to_f) / args[:spread]
28
+ end
29
+
30
+ self.class.new(label, norm_points, color, custom)
31
+ end
32
+ end
33
+ end
34
+ end