scruffy 0.1.0 → 0.2.0
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.
- 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
|