scruffy 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +37 -2
- data/lib/scruffy.rb +4 -6
- data/lib/scruffy/components.rb +11 -0
- data/lib/scruffy/components/background.rb +24 -0
- data/lib/scruffy/components/base.rb +46 -0
- data/lib/scruffy/components/data_markers.rb +25 -0
- data/lib/scruffy/components/graphs.rb +48 -0
- data/lib/scruffy/components/grid.rb +14 -0
- data/lib/scruffy/components/label.rb +12 -0
- data/lib/scruffy/components/legend.rb +64 -0
- data/lib/scruffy/components/style_info.rb +17 -0
- data/lib/scruffy/components/title.rb +12 -0
- data/lib/scruffy/components/value_markers.rb +31 -0
- data/lib/scruffy/components/viewport.rb +30 -0
- data/lib/scruffy/formatters.rb +111 -0
- data/lib/scruffy/graph.rb +38 -68
- data/lib/scruffy/helpers.rb +2 -0
- data/lib/scruffy/helpers/canvas.rb +22 -0
- data/lib/scruffy/helpers/layer_container.rb +69 -0
- data/lib/scruffy/layers.rb +1 -0
- data/lib/scruffy/layers/all_smiles.rb +4 -0
- data/lib/scruffy/layers/area.rb +3 -2
- data/lib/scruffy/layers/average.rb +3 -12
- data/lib/scruffy/layers/bar.rb +1 -1
- data/lib/scruffy/layers/base.rb +35 -20
- data/lib/scruffy/layers/line.rb +2 -2
- data/lib/scruffy/layers/stacked.rb +75 -0
- data/lib/scruffy/rasterizers.rb +2 -1
- data/lib/scruffy/rasterizers/batik_rasterizer.rb +25 -0
- data/lib/scruffy/rasterizers/rmagick_rasterizer.rb +6 -1
- data/lib/scruffy/renderers.rb +5 -0
- data/lib/scruffy/renderers/base.rb +37 -0
- data/lib/scruffy/renderers/cubed.rb +36 -0
- data/lib/scruffy/renderers/reversed.rb +18 -0
- data/lib/scruffy/renderers/split.rb +34 -0
- data/lib/scruffy/renderers/standard.rb +14 -152
- data/lib/scruffy/themes.rb +63 -19
- data/lib/scruffy/version.rb +1 -1
- data/spec/graph_spec.rb +33 -1
- data/spec/layers/line_spec.rb +9 -0
- metadata +26 -4
- data/lib/scruffy/resolver.rb +0 -14
- data/lib/scruffy/transformer.rb +0 -73
data/lib/scruffy/graph.rb
CHANGED
@@ -73,12 +73,14 @@ module Scruffy
|
|
73
73
|
# Of course, while you may be able to combine some things such as pie charts and line graphs, that
|
74
74
|
# doesn't necessarily mean they will make any logical sense together. We leave those decisions up to you. :)
|
75
75
|
class Graph
|
76
|
+
include Scruffy::Helpers::LayerContainer
|
77
|
+
|
76
78
|
attr_accessor :title
|
77
79
|
attr_accessor :theme
|
78
|
-
attr_accessor :layers
|
79
80
|
attr_accessor :default_type
|
80
81
|
attr_accessor :point_markers
|
81
|
-
attr_accessor :
|
82
|
+
attr_accessor :value_formatter
|
83
|
+
attr_accessor :rasterizer
|
82
84
|
|
83
85
|
attr_reader :renderer # Writer defined below
|
84
86
|
|
@@ -94,20 +96,21 @@ module Scruffy
|
|
94
96
|
# theme:: A theme hash to use when rendering graph
|
95
97
|
# layers:: An array of Layers for this graph to use
|
96
98
|
# default_type:: A symbol indicating the default type of Layer for this graph
|
97
|
-
#
|
98
|
-
# marker_transformer:: Sets a transformer used to modify marker values prior to rendering
|
99
|
+
# value_formatter:: Sets a formatter used to modify marker values prior to rendering
|
99
100
|
# point_markers:: Sets the x-axis marker values
|
101
|
+
# rasterizer:: Sets the rasterizer to use when rendering to an image format. Defaults to RMagick.
|
100
102
|
def initialize(*args)
|
101
103
|
self.default_type = args.shift if args.first.is_a?(Symbol)
|
102
104
|
options = args.shift.dup if args.first.is_a?(Hash)
|
103
105
|
raise ArgumentError, "The arguments provided are not supported." if args.size > 0
|
104
106
|
|
105
107
|
options ||= {}
|
106
|
-
self.theme = Scruffy::Themes::
|
107
|
-
self.
|
108
|
-
self.
|
108
|
+
self.theme = Scruffy::Themes::Keynote.new
|
109
|
+
self.renderer = Scruffy::Renderers::Standard.new
|
110
|
+
self.rasterizer = Scruffy::Rasterizers::RMagickRasterizer.new
|
111
|
+
self.value_formatter = Scruffy::Formatters::Number.new
|
109
112
|
|
110
|
-
%w(title theme layers default_type
|
113
|
+
%w(title theme layers default_type value_formatter point_markers rasterizer).each do |arg|
|
111
114
|
self.send("#{arg}=".to_sym, options.delete(arg.to_sym)) unless options[arg.to_sym].nil?
|
112
115
|
end
|
113
116
|
|
@@ -118,7 +121,7 @@ module Scruffy
|
|
118
121
|
#
|
119
122
|
# Options:
|
120
123
|
# size:: An array indicating the size you wish to render the graph. ( [x, y] )
|
121
|
-
# width:: The width of the rendered graph. A height is calculated at
|
124
|
+
# width:: The width of the rendered graph. A height is calculated at 3/4th of the width.
|
122
125
|
# theme:: Theme used to render graph for this render only.
|
123
126
|
# min_value:: Overrides the calculated minimum value used for the graph.
|
124
127
|
# max_value:: Overrides the calculated maximum value used for the graph.
|
@@ -127,75 +130,42 @@ module Scruffy
|
|
127
130
|
# as:: File format to render to ('PNG', 'JPG', etc)
|
128
131
|
# to:: Name of file to save graph to, if desired. If not provided, image is returned as blob.
|
129
132
|
def render(options = {})
|
130
|
-
|
131
|
-
options[:
|
132
|
-
options[:
|
133
|
-
options[:
|
133
|
+
options[:theme] ||= theme
|
134
|
+
options[:value_formatter] ||= value_formatter
|
135
|
+
options[:point_markers] ||= point_markers
|
136
|
+
options[:size] ||= (options[:width] ? [options[:width], (options.delete(:width) * 0.6).to_i] : [600, 360])
|
137
|
+
options[:title] ||= title
|
138
|
+
options[:layers] ||= layers
|
139
|
+
options[:min_value] ||= bottom_value(:padded)
|
140
|
+
options[:max_value] ||= top_value
|
141
|
+
options[:graph] ||= self
|
142
|
+
|
134
143
|
|
135
|
-
|
136
|
-
|
137
|
-
|
144
|
+
if options[:as] && (options[:size][0] <= 300 || options[:size][1] <= 200)
|
145
|
+
options[:actual_size] = options[:size]
|
146
|
+
options[:size] = [800, (800.to_f * (options[:actual_size][1].to_f / options[:actual_size][0].to_f))]
|
147
|
+
puts options[:size].inspect
|
148
|
+
end
|
149
|
+
|
150
|
+
svg = ( options[:renderer].nil? ? self.renderer.render( options ) : options[:renderer].render( options ) )
|
138
151
|
|
139
|
-
options[:
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
#
|
147
|
-
# Both #add and #<< can be used.
|
148
|
-
#
|
149
|
-
# graph.add(:line, [100, 200, 150]) # Create and add an untitled line graph
|
150
|
-
#
|
151
|
-
# graph << (:line, "John's Sales", [150, 100]) # Create and add a titled line graph
|
152
|
-
#
|
153
|
-
# graph << Scruffy::Layers::Bar.new({...}) # Adds Bar layer to graph
|
154
|
-
#
|
155
|
-
def <<(*args)
|
156
|
-
if args[0].kind_of?(Scruffy::Layers::Base)
|
157
|
-
layers << args[0]
|
158
|
-
else
|
159
|
-
type = args.first.is_a?(Symbol) ? args.shift : default_type
|
160
|
-
title = args.shift if args.first.is_a?(String)
|
161
|
-
points = args.first.is_a?(Array) ? args.shift : []
|
162
|
-
options = args.first.is_a?(Hash) ? args.shift : {}
|
163
|
-
|
164
|
-
title ||= ''
|
165
|
-
|
166
|
-
raise ArgumentError,
|
167
|
-
'You must specify a graph type (:area, :bar, :line, etc) if you do not have a default type specified.' if type.nil?
|
168
|
-
|
169
|
-
layer = Kernel::module_eval("Scruffy::Layers::#{to_camelcase(type.to_s)}").new(options.merge({:points => points, :title => title}))
|
170
|
-
layers << layer
|
152
|
+
options[:size] = options[:actual_size] if options[:actual_size]
|
153
|
+
|
154
|
+
# SVG to file.
|
155
|
+
if options[:to] && options[:as].nil?
|
156
|
+
File.open(options[:to], 'w') { |file|
|
157
|
+
file.write(svg)
|
158
|
+
}
|
171
159
|
end
|
172
|
-
|
160
|
+
|
161
|
+
options[:as] ? rasterizer.rasterize(svg, options) : svg
|
173
162
|
end
|
174
163
|
|
175
|
-
alias :add :<<
|
176
164
|
|
177
165
|
def renderer=(val)
|
178
166
|
raise ArgumentError, "Renderer must include a #render(options) method." unless (val.respond_to?(:render) && val.method(:render).arity.abs > 0)
|
179
167
|
|
180
168
|
@renderer = val
|
181
169
|
end
|
182
|
-
|
183
|
-
protected
|
184
|
-
def to_camelcase(type) # :nodoc:
|
185
|
-
type.split('_').map { |e| e.capitalize }.join('')
|
186
|
-
end
|
187
|
-
|
188
|
-
def top_value # :nodoc:
|
189
|
-
layers.inject(0) { |max, layer| (max = ((max < layer.highest_point) ? layer.highest_point : max)) unless layer.highest_point.nil?; max }
|
190
|
-
end
|
191
|
-
|
192
|
-
def bottom_value # :nodoc:
|
193
|
-
botval = layers.inject(top_value) { |min, layer| (min = ((min > layer.lowest_point) ? layer.lowest_point : min)) unless layer.lowest_point.nil?; min }
|
194
|
-
above_zero = (botval > 0)
|
195
|
-
|
196
|
-
botval = (botval - ((top_value - botval) * 0.15))
|
197
|
-
botval = 0 if (above_zero && botval < 0)
|
198
|
-
botval
|
199
|
-
end
|
200
170
|
end
|
201
171
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Scruffy
|
2
|
+
module Helpers
|
3
|
+
|
4
|
+
# Helper methods for objects that act as canvases/viewports/etc.
|
5
|
+
#
|
6
|
+
# Primarily used for calculating relative positions.
|
7
|
+
module Canvas
|
8
|
+
|
9
|
+
protected
|
10
|
+
def bounds_for(canvas_size, position, size)
|
11
|
+
return nil if (position.nil? || size.nil?)
|
12
|
+
bounds = {}
|
13
|
+
bounds[:x] = canvas_size.first * (position.first / 100.to_f)
|
14
|
+
bounds[:y] = canvas_size.last * (position.last / 100.to_f)
|
15
|
+
bounds[:width] = canvas_size.first * (size.first / 100.to_f)
|
16
|
+
bounds[:height] = canvas_size.last * (size.last / 100.to_f)
|
17
|
+
bounds
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Scruffy::Helpers
|
2
|
+
module LayerContainer
|
3
|
+
# Adds a Layer to the Graph/Container. Accepts either a list of arguments used to build a new layer, or
|
4
|
+
# a Scruffy::Layers::Base-derived object. When passing a list of arguments, all arguments are optional, but the arguments
|
5
|
+
# specified must be provided in a particular order: type (Symbol), title (String), points (Array),
|
6
|
+
# options (Hash).
|
7
|
+
#
|
8
|
+
# Both #add and #<< can be used.
|
9
|
+
#
|
10
|
+
# graph.add(:line, [100, 200, 150]) # Create and add an untitled line graph
|
11
|
+
#
|
12
|
+
# graph << (:line, "John's Sales", [150, 100]) # Create and add a titled line graph
|
13
|
+
#
|
14
|
+
# graph << Scruffy::Layers::Bar.new({...}) # Adds Bar layer to graph
|
15
|
+
#
|
16
|
+
def <<(*args, &block)
|
17
|
+
if args[0].kind_of?(Scruffy::Layers::Base)
|
18
|
+
layers << args[0]
|
19
|
+
else
|
20
|
+
type = args.first.is_a?(Symbol) ? args.shift : @default_type
|
21
|
+
title = args.shift if args.first.is_a?(String)
|
22
|
+
points = args.first.is_a?(Array) ? args.shift : []
|
23
|
+
options = args.first.is_a?(Hash) ? args.shift : {}
|
24
|
+
|
25
|
+
title ||= ''
|
26
|
+
|
27
|
+
raise ArgumentError,
|
28
|
+
'You must specify a graph type (:area, :bar, :line, etc) if you do not have a default type specified.' if type.nil?
|
29
|
+
|
30
|
+
layer = Kernel::module_eval("Scruffy::Layers::#{to_camelcase(type.to_s)}").new(options.merge({:points => points, :title => title}), &block)
|
31
|
+
layers << layer
|
32
|
+
end
|
33
|
+
layer
|
34
|
+
end
|
35
|
+
|
36
|
+
alias :add :<<
|
37
|
+
|
38
|
+
|
39
|
+
# Custom Layer Accessors
|
40
|
+
def layers=(val)
|
41
|
+
@layers = val
|
42
|
+
end
|
43
|
+
def layers
|
44
|
+
@layers ||= []
|
45
|
+
end
|
46
|
+
|
47
|
+
def top_value(padding=nil) # :nodoc:
|
48
|
+
topval = layers.inject(0) { |max, layer| (max = ((max < layer.top_value) ? layer.top_value : max)) unless layer.top_value.nil?; max }
|
49
|
+
padding == :padded ? (topval - ((topval - bottom_value) * 0.15)) : topval
|
50
|
+
end
|
51
|
+
|
52
|
+
def bottom_value(padding=nil) # :nodoc:
|
53
|
+
botval = layers.inject(top_value) { |min, layer| (min = ((min > layer.bottom_value) ? layer.bottom_value : min)) unless layer.bottom_value.nil?; min }
|
54
|
+
above_zero = (botval > 0)
|
55
|
+
botval = (botval - ((top_value - botval) * 0.15))
|
56
|
+
|
57
|
+
# Don't introduce negative values solely due to padding.
|
58
|
+
# A user-provided value must be negative before padding will extend into negative values.
|
59
|
+
(above_zero && botval < 0) ? 0 : botval
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
protected
|
64
|
+
def to_camelcase(type) # :nodoc:
|
65
|
+
type.split('_').map { |e| e.capitalize }.join('')
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
data/lib/scruffy/layers.rb
CHANGED
data/lib/scruffy/layers/area.rb
CHANGED
@@ -5,6 +5,7 @@ module Scruffy
|
|
5
5
|
points_value = "0,#{height} #{stringify_coords(coords).join(' ')} #{width},#{height}"
|
6
6
|
|
7
7
|
# Experimental, for later user.
|
8
|
+
# Neither ImageMagick nor Mozilla SVG render this well (at all). Maybe a future thing.
|
8
9
|
#
|
9
10
|
# svg.defs {
|
10
11
|
# svg.filter(:id => 'MyFilter', :filterUnits => 'userSpaceOnUse', :x => 0, :y => 0, :width => 200, :height => '120') {
|
@@ -26,8 +27,8 @@ module Scruffy
|
|
26
27
|
# }
|
27
28
|
# }
|
28
29
|
# }
|
29
|
-
|
30
|
-
svg.polygon(:points => points_value, :fill => color.to_s, :stroke => color.to_s,
|
30
|
+
|
31
|
+
svg.polygon(:points => points_value, :fill => color.to_s, :stroke => color.to_s, 'style' => "opacity: #{opacity}")
|
31
32
|
end
|
32
33
|
end
|
33
34
|
end
|
@@ -2,21 +2,12 @@ module Scruffy
|
|
2
2
|
module Layers
|
3
3
|
class Average < Base
|
4
4
|
def initialize(options = {})
|
5
|
-
super
|
6
|
-
|
7
|
-
@relevant_data = false
|
5
|
+
super(options.merge({:relevant_data => false}))
|
8
6
|
end
|
7
|
+
|
9
8
|
def draw(svg, coords, options = {})
|
10
9
|
svg.polyline( :points => coords.join(' '), :fill => 'none', :stroke => 'black',
|
11
|
-
'stroke-width' =>
|
12
|
-
end
|
13
|
-
|
14
|
-
def highest_point
|
15
|
-
nil
|
16
|
-
end
|
17
|
-
|
18
|
-
def lowest_point
|
19
|
-
nil
|
10
|
+
'stroke-width' => relative(5), 'opacity' => '0.4')
|
20
11
|
end
|
21
12
|
|
22
13
|
protected
|
data/lib/scruffy/layers/bar.rb
CHANGED
@@ -7,7 +7,7 @@ module Scruffy
|
|
7
7
|
x, y, bar_height = (coord.first-(@bar_width * 0.5)), coord.last, (height - coord.last)
|
8
8
|
|
9
9
|
svg.rect( :x => x, :y => y, :width => @bar_width, :height => bar_height,
|
10
|
-
:fill => color.to_s, :stroke => color.to_s,
|
10
|
+
:fill => color.to_s, :stroke => color.to_s, 'style' => "opacity: #{opacity}" )
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
data/lib/scruffy/layers/base.rb
CHANGED
@@ -35,6 +35,7 @@ module Scruffy
|
|
35
35
|
attr_accessor :resolver
|
36
36
|
attr_accessor :complexity
|
37
37
|
attr_accessor :relevant_data
|
38
|
+
attr_accessor :options # On-the-fly values for easy customization / acts as attributes.
|
38
39
|
|
39
40
|
# Returns a new Base object.
|
40
41
|
#
|
@@ -45,10 +46,11 @@ module Scruffy
|
|
45
46
|
# relevant_data:: Rarely used - indicates the data on this graph should not
|
46
47
|
# included in any graph data aggregations, such as averaging data points.
|
47
48
|
def initialize(options = {})
|
48
|
-
@title = options
|
49
|
-
@points = options
|
50
|
-
@preferred_color = options
|
51
|
-
@relevant_data = options
|
49
|
+
@title = options.delete(:title) || ''
|
50
|
+
@points = options.delete(:points) || []
|
51
|
+
@preferred_color = options.delete(:color)
|
52
|
+
@relevant_data = options.delete(:relevant_data) || true
|
53
|
+
@options = options
|
52
54
|
end
|
53
55
|
|
54
56
|
# Builds SVG code for this graph using the provided Builder object.
|
@@ -72,28 +74,45 @@ module Scruffy
|
|
72
74
|
# This is what the various graphs need to implement.
|
73
75
|
end
|
74
76
|
|
77
|
+
# Returns a hash with information to be used by the legend.
|
78
|
+
#
|
79
|
+
# Alternatively, returns nil if you don't want this layer to be in the legend,
|
80
|
+
# or an array of hashes if this layer should have multiple legend entries (stacked?)
|
81
|
+
#
|
82
|
+
# By default, #legend_data returns nil automatically if relevant_data is set to false
|
83
|
+
# or the @color attribute is nil. @color is set when the layer is rendered, so legends
|
84
|
+
# must be rendered AFTER layers.
|
85
|
+
def legend_data
|
86
|
+
if relevant_data? && @color
|
87
|
+
{:title => title,
|
88
|
+
:color => @color,
|
89
|
+
:priority => :normal}
|
90
|
+
else
|
91
|
+
nil
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
75
95
|
# Returns the value of relevant_data
|
76
96
|
def relevant_data?
|
77
97
|
@relevant_data
|
78
98
|
end
|
79
99
|
|
80
|
-
# The highest data point on this layer
|
81
|
-
def
|
82
|
-
points.sort.last
|
100
|
+
# The highest data point on this layer, or nil if relevant_data == false
|
101
|
+
def top_value
|
102
|
+
@relevant_data ? points.sort.last : nil
|
83
103
|
end
|
84
104
|
|
85
|
-
# The lowest data point on this layer
|
86
|
-
def
|
87
|
-
points.sort.first
|
105
|
+
# The lowest data point on this layer, or nil if relevant_data == false
|
106
|
+
def bottom_value
|
107
|
+
@relevant_data ? points.sort.first: nil
|
88
108
|
end
|
89
|
-
|
109
|
+
|
90
110
|
protected
|
91
111
|
def setup_variables(options = {}) # :nodoc:
|
92
112
|
@color = (preferred_color || options.delete(:color))
|
93
|
-
@resolver = options.delete(:resolver)
|
94
113
|
@width, @height = options.delete(:size)
|
95
114
|
@min_value, @max_value = options[:min_value], options[:max_value]
|
96
|
-
@opacity = options[:opacity]
|
115
|
+
@opacity = options[:opacity] || 1.0
|
97
116
|
@complexity = options[:complexity]
|
98
117
|
end
|
99
118
|
|
@@ -112,13 +131,9 @@ module Scruffy
|
|
112
131
|
coords
|
113
132
|
end
|
114
133
|
|
115
|
-
#
|
116
|
-
|
117
|
-
|
118
|
-
#
|
119
|
-
# ie: On a graph that is 800px high, scaled(1) => 2. For 200px high, scaled(1) => 0.5, and so on...
|
120
|
-
def scaled(point)
|
121
|
-
resolver.to_point(point)
|
134
|
+
# Converts a percentage into a pixel value, related to the height.
|
135
|
+
def relative(pct)
|
136
|
+
@height * (pct / 100.to_f)
|
122
137
|
end
|
123
138
|
|
124
139
|
def stringify_coords(coords) # :nodoc:
|
data/lib/scruffy/layers/line.rb
CHANGED
@@ -3,9 +3,9 @@ module Scruffy
|
|
3
3
|
class Line < Base
|
4
4
|
def draw(svg, coords, options={})
|
5
5
|
svg.polyline( :points => stringify_coords(coords).join(' '), :fill => 'none',
|
6
|
-
:stroke => color.to_s, 'stroke-width' =>
|
6
|
+
:stroke => color.to_s, 'stroke-width' => relative(2) )
|
7
7
|
|
8
|
-
coords.each { |coord| svg.circle( :cx => coord.first, :cy => coord.last, :r =>
|
8
|
+
coords.each { |coord| svg.circle( :cx => coord.first, :cy => coord.last, :r => relative(2.5),
|
9
9
|
:fill => color.to_s ) }
|
10
10
|
end
|
11
11
|
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Scruffy
|
2
|
+
module Layers
|
3
|
+
class Stacked < Base
|
4
|
+
include Scruffy::Helpers::LayerContainer
|
5
|
+
|
6
|
+
def initialize(options={}, &block)
|
7
|
+
super(options)
|
8
|
+
|
9
|
+
block.call(self) # Allow for population of data with a block during initialization.
|
10
|
+
end
|
11
|
+
|
12
|
+
# Builds SVG code for this graph using the provided Builder object.
|
13
|
+
# This method actually generates data needed by this graph, then passes the
|
14
|
+
# rendering responsibilities to Base#draw.
|
15
|
+
#
|
16
|
+
# svg:: a Builder object used to create SVG code.
|
17
|
+
def render(svg, options = {})
|
18
|
+
current_points = points.dup
|
19
|
+
|
20
|
+
layers.each do |layer|
|
21
|
+
real_points = layer.points
|
22
|
+
layer.points = current_points
|
23
|
+
layer_options = options.dup
|
24
|
+
layer_options[:color] = layer.preferred_color || layer.color || options[:theme].next_color
|
25
|
+
layer.render(svg, layer_options)
|
26
|
+
options.merge(layer_options)
|
27
|
+
layer.points = real_points
|
28
|
+
|
29
|
+
layer.points.each_with_index { |val, idx| current_points[idx] -= val }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def legend_data
|
34
|
+
if relevant_data?
|
35
|
+
retval = []
|
36
|
+
layers.each do |layer|
|
37
|
+
retval << layer.legend_data
|
38
|
+
end
|
39
|
+
retval
|
40
|
+
else
|
41
|
+
nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# The highest data point on this layer, or nil if relevant_data == false
|
46
|
+
def top_value
|
47
|
+
@relevant_data ? points.sort.last : nil
|
48
|
+
end
|
49
|
+
|
50
|
+
def points
|
51
|
+
longest_arr = layers.inject(nil) do |longest, layer|
|
52
|
+
longest = layer.points if (longest.nil? || longest.size < layer.points.size)
|
53
|
+
end
|
54
|
+
|
55
|
+
summed_points = (0...longest_arr.size).map do |idx|
|
56
|
+
layers.inject(nil) do |sum, layer|
|
57
|
+
if sum.nil? && !layer.points[idx].nil?
|
58
|
+
sum = layer.points[idx]
|
59
|
+
elsif !layer.points[idx].nil?
|
60
|
+
sum += layer.points[idx]
|
61
|
+
end
|
62
|
+
|
63
|
+
sum
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
summed_points
|
68
|
+
end
|
69
|
+
|
70
|
+
def points=(val)
|
71
|
+
throw ArgumentsError, "Stacked layers cannot accept points, only other layers."
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|