scruffy 0.2.0 → 0.2.1
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 +8 -0
- data/Rakefile +9 -2
- data/lib/scruffy.rb +13 -0
- data/lib/scruffy/components.rb +10 -0
- data/lib/scruffy/components/viewport.rb +29 -17
- data/lib/scruffy/formatters.rb +156 -91
- data/lib/scruffy/graph.rb +22 -24
- data/lib/scruffy/helpers.rb +8 -0
- data/lib/scruffy/helpers/canvas.rb +25 -22
- data/lib/scruffy/helpers/layer_container.rb +20 -2
- data/lib/scruffy/layers.rb +15 -1
- data/lib/scruffy/layers/all_smiles.rb +121 -97
- data/lib/scruffy/layers/area.rb +38 -30
- data/lib/scruffy/layers/average.rb +52 -35
- data/lib/scruffy/layers/bar.rb +35 -22
- data/lib/scruffy/layers/base.rb +138 -126
- data/lib/scruffy/layers/line.rb +15 -9
- data/lib/scruffy/layers/sparkline_bar.rb +38 -0
- data/lib/scruffy/layers/stacked.rb +74 -59
- data/lib/scruffy/rasterizers.rb +13 -1
- data/lib/scruffy/rasterizers/batik_rasterizer.rb +35 -21
- data/lib/scruffy/rasterizers/rmagick_rasterizer.rb +19 -22
- data/lib/scruffy/renderers.rb +17 -1
- data/lib/scruffy/renderers/base.rb +39 -31
- data/lib/scruffy/renderers/cubed.rb +39 -31
- data/lib/scruffy/renderers/cubed3d.rb +53 -0
- data/lib/scruffy/renderers/empty.rb +23 -0
- data/lib/scruffy/renderers/sparkline.rb +11 -0
- data/lib/scruffy/themes.rb +81 -61
- data/lib/scruffy/version.rb +1 -1
- metadata +16 -4
data/lib/scruffy/layers/bar.rb
CHANGED
@@ -1,30 +1,43 @@
|
|
1
|
-
module Scruffy
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
1
|
+
module Scruffy::Layers
|
2
|
+
# ==Scruffy::Layers::Bar
|
3
|
+
#
|
4
|
+
# Author:: Brasten Sager
|
5
|
+
# Date:: August 6th, 2006
|
6
|
+
#
|
7
|
+
# Standard bar graph.
|
8
|
+
class Bar < Base
|
9
|
+
|
10
|
+
# Draw bar graph.
|
11
|
+
def draw(svg, coords, options = {})
|
12
|
+
coords.each do |coord|
|
13
|
+
x, y, bar_height = (coord.first-(@bar_width * 0.5)), coord.last, (height - coord.last)
|
8
14
|
|
9
|
-
|
10
|
-
|
11
|
-
end
|
15
|
+
svg.rect( :x => x, :y => y, :width => @bar_width, :height => bar_height,
|
16
|
+
:fill => color.to_s, 'style' => "opacity: #{opacity}; stroke: none;" )
|
12
17
|
end
|
18
|
+
end
|
13
19
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
20
|
+
protected
|
21
|
+
|
22
|
+
# Due to the size of the bar graph, X-axis coords must
|
23
|
+
# be squeezed so that the bars do not hang off the ends
|
24
|
+
# of the graph.
|
25
|
+
#
|
26
|
+
# Unfortunately this just mean that bar-graphs and most other graphs
|
27
|
+
# end up on different points. Maybe adding a padding to the coordinates
|
28
|
+
# should be a graph-wide thing?
|
29
|
+
def generate_coordinates(options = {})
|
30
|
+
@bar_width = (width / points.size) * 0.9
|
31
|
+
options[:point_distance] = (width - (width / points.size)) / (points.size - 1).to_f
|
18
32
|
|
19
|
-
|
20
|
-
|
33
|
+
coords = (0...points.size).map do |idx|
|
34
|
+
x_coord = (options[:point_distance] * idx) + (width / points.size * 0.5)
|
21
35
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
end
|
26
|
-
coords
|
36
|
+
relative_percent = ((points[idx] == min_value) ? 0 : ((points[idx] - min_value) / (max_value - min_value).to_f))
|
37
|
+
y_coord = (height - (height * relative_percent))
|
38
|
+
[x_coord, y_coord]
|
27
39
|
end
|
28
|
-
|
40
|
+
coords
|
41
|
+
end
|
29
42
|
end
|
30
43
|
end
|
data/lib/scruffy/layers/base.rb
CHANGED
@@ -1,144 +1,156 @@
|
|
1
|
-
module Scruffy
|
2
|
-
# ==Scruffy::Layers
|
1
|
+
module Scruffy::Layers
|
2
|
+
# ==Scruffy::Layers::Base
|
3
3
|
#
|
4
4
|
# Author:: Brasten Sager
|
5
|
-
# Date:: August
|
5
|
+
# Date:: August 5th, 2006
|
6
6
|
#
|
7
|
-
#
|
7
|
+
# Scruffy::Layers::Base contains the basic functionality needed by the various types of graphs. The Base
|
8
|
+
# class is responsible holding layer information such as the title and data points.
|
8
9
|
#
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
# When the graph is rendered, the graph renderer calls Base#render. Base#render sets up
|
11
|
+
# some standard information, and calculates the x,y coordinates of each data point. The draw() method,
|
12
|
+
# which should have been overridden by the current instance, is then called. The actual rendering of
|
13
|
+
# the graph takes place there.
|
14
|
+
#
|
15
|
+
# ====Create New Graph Types
|
16
|
+
#
|
17
|
+
# Assuming the information generated by Scruffy::Layers::Base is sufficient, you can create a new graph type
|
18
|
+
# simply by overriding the draw() method. See Base#draw for arguments.
|
19
|
+
#
|
20
|
+
class Base
|
21
|
+
# The following attributes are user-definable at any time.
|
22
|
+
# title, points, relevant_data, preferred_color, options
|
23
|
+
attr_accessor :title
|
24
|
+
attr_accessor :points
|
25
|
+
attr_accessor :relevant_data
|
26
|
+
attr_accessor :preferred_color
|
27
|
+
attr_accessor :options # On-the-fly values for easy customization / acts as attributes.
|
28
|
+
|
29
|
+
# The following attributes are set during the layer's render process,
|
30
|
+
# and act more as a record of what just happened for later processes.
|
31
|
+
# height, width, min_value, max_value, color, opacity, complexity
|
32
|
+
attr_reader :height, :width
|
33
|
+
attr_reader :min_value, :max_value
|
34
|
+
attr_reader :color
|
35
|
+
attr_reader :opacity
|
36
|
+
attr_reader :complexity
|
37
|
+
|
38
|
+
# Returns a new Base object.
|
14
39
|
#
|
15
|
-
#
|
16
|
-
#
|
40
|
+
# Any options other that those specified below are stored in the @options variable for
|
41
|
+
# possible later use. This would be a good place to store options needed for a custom
|
42
|
+
# graph.
|
17
43
|
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
44
|
+
# Options:
|
45
|
+
# title:: Name/title of data group
|
46
|
+
# points:: Array of data points
|
47
|
+
# preferred_color:: Color used to render this graph, overrides theme color.
|
48
|
+
# relevant_data:: Rarely used - indicates the data on this graph should not
|
49
|
+
# included in any graph data aggregations, such as averaging data points.
|
50
|
+
def initialize(options = {})
|
51
|
+
@title = options.delete(:title) || ''
|
52
|
+
@points = options.delete(:points) || []
|
53
|
+
@preferred_color = options.delete(:preferred_color)
|
54
|
+
@relevant_data = options.delete(:relevant_data) || true
|
55
|
+
@options = options
|
56
|
+
end
|
57
|
+
|
58
|
+
# Builds SVG code for this graph using the provided Builder object.
|
59
|
+
# This method actually generates data needed by this graph, then passes the
|
60
|
+
# rendering responsibilities to Base#draw.
|
22
61
|
#
|
23
|
-
#
|
62
|
+
# svg:: a Builder object used to create SVG code.
|
63
|
+
def render(svg, options = {})
|
64
|
+
setup_variables(options)
|
65
|
+
coords = generate_coordinates(options)
|
66
|
+
|
67
|
+
draw(svg, coords, options)
|
68
|
+
end
|
69
|
+
|
70
|
+
# The method called by Base#draw to render the graph.
|
71
|
+
#
|
72
|
+
# svg:: a Builder object to use for creating SVG code.
|
73
|
+
# coords:: An array of coordinates relating to the graph's data points. ie: [[100, 120], [200, 140], [300, 40]]
|
74
|
+
# options:: Optional arguments.
|
75
|
+
def draw(svg, coords, options={})
|
76
|
+
raise RenderError, "You must override the Base#draw method."
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns a hash with information to be used by the legend.
|
24
80
|
#
|
25
|
-
#
|
26
|
-
#
|
81
|
+
# Alternatively, returns nil if you don't want this layer to be in the legend,
|
82
|
+
# or an array of hashes if this layer should have multiple legend entries (stacked?)
|
27
83
|
#
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
attr_accessor :options # On-the-fly values for easy customization / acts as attributes.
|
39
|
-
|
40
|
-
# Returns a new Base object.
|
41
|
-
#
|
42
|
-
# Options:
|
43
|
-
# title::
|
44
|
-
# points:: Array of data points
|
45
|
-
# color:: Color used to render this graph, overrides theme color.
|
46
|
-
# relevant_data:: Rarely used - indicates the data on this graph should not
|
47
|
-
# included in any graph data aggregations, such as averaging data points.
|
48
|
-
def initialize(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
|
54
|
-
end
|
55
|
-
|
56
|
-
# Builds SVG code for this graph using the provided Builder object.
|
57
|
-
# This method actually generates data needed by this graph, then passes the
|
58
|
-
# rendering responsibilities to Base#draw.
|
59
|
-
#
|
60
|
-
# svg:: a Builder object used to create SVG code.
|
61
|
-
def render(svg, options = {})
|
62
|
-
setup_variables(options)
|
63
|
-
coords = generate_coordinates(options)
|
64
|
-
|
65
|
-
draw(svg, coords, options)
|
66
|
-
end
|
67
|
-
|
68
|
-
# The method called by Base#draw to render the graph.
|
69
|
-
#
|
70
|
-
# svg:: a Builder object to use for creating SVG code.
|
71
|
-
# coords:: An array of coordinates relating to the graph's data points. ie: [[100, 120], [200, 140], [300, 40]]
|
72
|
-
# options:: Optional arguments.
|
73
|
-
def draw(svg, coords, options={})
|
74
|
-
# This is what the various graphs need to implement.
|
75
|
-
end
|
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
|
-
|
95
|
-
# Returns the value of relevant_data
|
96
|
-
def relevant_data?
|
97
|
-
@relevant_data
|
98
|
-
end
|
99
|
-
|
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
|
103
|
-
end
|
104
|
-
|
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
|
84
|
+
# By default, #legend_data returns nil automatically if relevant_data is set to false
|
85
|
+
# or the @color attribute is nil. @color is set when the layer is rendered, so legends
|
86
|
+
# must be rendered AFTER layers.
|
87
|
+
def legend_data
|
88
|
+
if relevant_data? && @color
|
89
|
+
{:title => title,
|
90
|
+
:color => @color,
|
91
|
+
:priority => :normal}
|
92
|
+
else
|
93
|
+
nil
|
108
94
|
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Returns the value of relevant_data
|
98
|
+
def relevant_data?
|
99
|
+
@relevant_data
|
100
|
+
end
|
101
|
+
|
102
|
+
# The highest data point on this layer, or nil if relevant_data == false
|
103
|
+
def top_value
|
104
|
+
@relevant_data ? points.sort.last : nil
|
105
|
+
end
|
106
|
+
|
107
|
+
# The lowest data point on this layer, or nil if relevant_data == false
|
108
|
+
def bottom_value
|
109
|
+
@relevant_data ? points.sort.first: nil
|
110
|
+
end
|
109
111
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
112
|
+
protected
|
113
|
+
# Sets up several variables that almost every graph layer will need to render
|
114
|
+
# itself.
|
115
|
+
def setup_variables(options = {})
|
116
|
+
@color = (preferred_color || options.delete(:color))
|
117
|
+
@width, @height = options.delete(:size)
|
118
|
+
@min_value, @max_value = options[:min_value], options[:max_value]
|
119
|
+
@opacity = options[:opacity] || 1.0
|
120
|
+
@complexity = options[:complexity]
|
121
|
+
end
|
118
122
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
123
|
+
# Optimistic generation of coordinates for layer to use. These coordinates are
|
124
|
+
# just a best guess, and can be overridden or thrown away (for example, this is overridden
|
125
|
+
# in pie charting and bar charts).
|
126
|
+
def generate_coordinates(options = {})
|
127
|
+
options[:point_distance] = width / (points.size - 1).to_f
|
128
|
+
|
129
|
+
coords = (0...points.size).map do |idx|
|
130
|
+
x_coord = options[:point_distance] * idx
|
124
131
|
|
125
|
-
|
126
|
-
|
132
|
+
relative_percent = ((points[idx] == min_value) ? 0 : ((points[idx] - min_value) / (max_value - min_value).to_f))
|
133
|
+
y_coord = (height - (height * relative_percent))
|
127
134
|
|
128
|
-
|
129
|
-
end
|
130
|
-
|
131
|
-
coords
|
135
|
+
[x_coord, y_coord]
|
132
136
|
end
|
133
137
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
+
coords
|
139
|
+
end
|
140
|
+
|
141
|
+
# Converts a percentage into a pixel value, relative to the height.
|
142
|
+
#
|
143
|
+
# Example:
|
144
|
+
# relative(5) # On a 100px high layer, this returns 5. 200px high layer, this returns 10, etc.
|
145
|
+
def relative(pct)
|
146
|
+
@height * (pct / 100.to_f)
|
147
|
+
end
|
138
148
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
149
|
+
# Some SVG elements take a long string of multiple coordinates. This is here
|
150
|
+
# to make that a little easier.
|
151
|
+
def stringify_coords(coords) # :nodoc:
|
152
|
+
coords.map { |c| c.join(',') }
|
153
|
+
end
|
143
154
|
end
|
144
|
-
|
155
|
+
|
156
|
+
end # scruffy::layers
|
data/lib/scruffy/layers/line.rb
CHANGED
@@ -1,13 +1,19 @@
|
|
1
|
-
module Scruffy
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
1
|
+
module Scruffy::Layers
|
2
|
+
# ==Scruffy::Layers::Line
|
3
|
+
#
|
4
|
+
# Author:: Brasten Sager
|
5
|
+
# Date:: August 7th, 2006
|
6
|
+
#
|
7
|
+
# Line graph.
|
8
|
+
class Line < Base
|
9
|
+
|
10
|
+
# Renders line graph.
|
11
|
+
def draw(svg, coords, options={})
|
12
|
+
svg.polyline( :points => stringify_coords(coords).join(' '), :fill => 'none',
|
13
|
+
:stroke => color.to_s, 'stroke-width' => relative(2) )
|
7
14
|
|
8
|
-
|
9
|
-
|
10
|
-
end
|
15
|
+
coords.each { |coord| svg.circle( :cx => coord.first, :cy => coord.last, :r => relative(2.5),
|
16
|
+
:fill => color.to_s ) }
|
11
17
|
end
|
12
18
|
end
|
13
19
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Scruffy
|
2
|
+
module Layers
|
3
|
+
# Experimental, do not use.
|
4
|
+
class SparklineBar < Base
|
5
|
+
|
6
|
+
def draw(svg, coords, options = {})
|
7
|
+
zero_point = @height / 2.0
|
8
|
+
|
9
|
+
coords.each do |coord|
|
10
|
+
x, y, bar_height = (coord.first-(@bar_width * 0.5)), coord.last, (height - coord.last)
|
11
|
+
|
12
|
+
bar_color = (y > zero_point) ? 'black' : 'red'
|
13
|
+
bar_height = (bar_height - zero_point)
|
14
|
+
|
15
|
+
#y = (bar_height < 0) ?
|
16
|
+
|
17
|
+
# svg.rect( :x => x, :y => zero_point, :width => @bar_width, :height => ,
|
18
|
+
# :fill => bar_color, :stroke => 'none', 'style' => "opacity: #{opacity}" )
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
def generate_coordinates(options = {})
|
24
|
+
@bar_width = (width / points.size) * 0.9
|
25
|
+
options[:point_distance] = (width - (width / points.size)) / (points.size - 1).to_f
|
26
|
+
|
27
|
+
coords = (0...points.size).map do |idx|
|
28
|
+
x_coord = (options[:point_distance] * idx) + (width / points.size * 0.5)
|
29
|
+
|
30
|
+
relative_percent = ((points[idx] == min_value) ? 0 : ((points[idx] - min_value) / (max_value - min_value).to_f))
|
31
|
+
y_coord = (height - (height * relative_percent))
|
32
|
+
[x_coord, y_coord]
|
33
|
+
end
|
34
|
+
coords
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -1,75 +1,90 @@
|
|
1
|
-
module Scruffy
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
1
|
+
module Scruffy::Layers
|
2
|
+
# ==Scruffy::Layers::Stacked
|
3
|
+
#
|
4
|
+
# Author:: Brasten Sager
|
5
|
+
# Date:: August 12th, 2006
|
6
|
+
#
|
7
|
+
# Provides a generic way for stacking graphs. This may or may not
|
8
|
+
# do what you'd expect under every situation, but it at least kills
|
9
|
+
# a couple birds with one stone (stacked bar graphs and stacked area
|
10
|
+
# graphs work fine).
|
11
|
+
class Stacked < Base
|
12
|
+
include Scruffy::Helpers::LayerContainer
|
13
|
+
|
14
|
+
# Returns new Stacked graph.
|
15
|
+
#
|
16
|
+
# You can provide a block for easily adding layers during (just after) initialization.
|
17
|
+
# Example:
|
18
|
+
# Stacked.new do |stacked|
|
19
|
+
# stacked << Scruffy::Layers::Line.new( ... )
|
20
|
+
# stacked.add(:bar, 'My Bar', [...])
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# The initialize method passes itself to the block, and since stacked is a LayerContainer,
|
24
|
+
# layers can be added just as if they were being added to Graph.
|
25
|
+
def initialize(options={}, &block)
|
26
|
+
super(options)
|
8
27
|
|
9
|
-
|
10
|
-
|
28
|
+
block.call(self) # Allow for population of data with a block during initialization.
|
29
|
+
end
|
30
|
+
|
31
|
+
# Overrides Base#render to fiddle with layers' points to achieve a stacked effect.
|
32
|
+
def render(svg, options = {})
|
33
|
+
current_points = points.dup
|
11
34
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
35
|
+
layers.each do |layer|
|
36
|
+
real_points = layer.points
|
37
|
+
layer.points = current_points
|
38
|
+
layer_options = options.dup
|
39
|
+
layer_options[:color] = layer.preferred_color || layer.color || options[:theme].next_color
|
40
|
+
layer.render(svg, layer_options)
|
41
|
+
options.merge(layer_options)
|
42
|
+
layer.points = real_points
|
19
43
|
|
20
|
-
|
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
|
44
|
+
layer.points.each_with_index { |val, idx| current_points[idx] -= val }
|
31
45
|
end
|
46
|
+
end
|
32
47
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
retval
|
40
|
-
else
|
41
|
-
nil
|
48
|
+
# A stacked graph has many data sets. Return legend information for all of them.
|
49
|
+
def legend_data
|
50
|
+
if relevant_data?
|
51
|
+
retval = []
|
52
|
+
layers.each do |layer|
|
53
|
+
retval << layer.legend_data
|
42
54
|
end
|
55
|
+
retval
|
56
|
+
else
|
57
|
+
nil
|
43
58
|
end
|
59
|
+
end
|
44
60
|
|
45
|
-
|
46
|
-
|
47
|
-
|
61
|
+
# The highest data point on this layer, or nil if relevant_data == false
|
62
|
+
def top_value
|
63
|
+
@relevant_data ? points.sort.last : nil
|
64
|
+
end
|
65
|
+
|
66
|
+
def points
|
67
|
+
longest_arr = layers.inject(nil) do |longest, layer|
|
68
|
+
longest = layer.points if (longest.nil? || longest.size < layer.points.size)
|
48
69
|
end
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
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
|
70
|
+
|
71
|
+
summed_points = (0...longest_arr.size).map do |idx|
|
72
|
+
layers.inject(nil) do |sum, layer|
|
73
|
+
if sum.nil? && !layer.points[idx].nil?
|
74
|
+
sum = layer.points[idx]
|
75
|
+
elsif !layer.points[idx].nil?
|
76
|
+
sum += layer.points[idx]
|
64
77
|
end
|
78
|
+
|
79
|
+
sum
|
65
80
|
end
|
66
|
-
|
67
|
-
summed_points
|
68
81
|
end
|
69
82
|
|
70
|
-
|
71
|
-
|
72
|
-
|
83
|
+
summed_points
|
84
|
+
end
|
85
|
+
|
86
|
+
def points=(val)
|
87
|
+
throw ArgumentsError, "Stacked layers cannot accept points, only other layers."
|
73
88
|
end
|
74
89
|
end
|
75
90
|
end
|