scruffy 0.2.6 → 0.3.0.beta1
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/{History.txt → CHANGES.txt} +15 -12
- data/README.txt +25 -25
- data/lib/scruffy.rb +0 -5
- data/lib/scruffy/components.rb +1 -0
- data/lib/scruffy/components/axes.rb +23 -0
- data/lib/scruffy/components/background.rb +3 -3
- data/lib/scruffy/components/base.rb +3 -0
- data/lib/scruffy/components/data_markers.rb +23 -8
- data/lib/scruffy/components/graphs.rb +4 -0
- data/lib/scruffy/components/grid.rb +45 -4
- data/lib/scruffy/components/legend.rb +63 -21
- data/lib/scruffy/components/title.rb +1 -1
- data/lib/scruffy/components/value_markers.rb +9 -16
- data/lib/scruffy/formatters.rb +41 -3
- data/lib/scruffy/graph.rb +27 -11
- data/lib/scruffy/graph_state.rb +5 -0
- data/lib/scruffy/helpers.rb +1 -0
- data/lib/scruffy/helpers/layer_container.rb +28 -4
- data/lib/scruffy/helpers/marker_helper.rb +25 -0
- data/lib/scruffy/helpers/point_container.rb +46 -17
- data/lib/scruffy/layers.rb +6 -1
- data/lib/scruffy/layers/bar.rb +35 -14
- data/lib/scruffy/layers/base.rb +51 -21
- data/lib/scruffy/layers/box.rb +114 -0
- data/lib/scruffy/layers/line.rb +31 -14
- data/lib/scruffy/layers/multi.rb +74 -0
- data/lib/scruffy/layers/multi_area.rb +119 -0
- data/lib/scruffy/layers/multi_bar.rb +51 -0
- data/lib/scruffy/layers/scatter.rb +13 -5
- data/lib/scruffy/layers/stacked.rb +2 -1
- data/lib/scruffy/rasterizers.rb +1 -0
- data/lib/scruffy/rasterizers/mini_magick_rasterizer.rb +24 -0
- data/lib/scruffy/rasterizers/rmagick_rasterizer.rb +8 -2
- data/lib/scruffy/renderers.rb +2 -0
- data/lib/scruffy/renderers/axis_legend.rb +41 -0
- data/lib/scruffy/renderers/base.rb +6 -4
- data/lib/scruffy/renderers/basic.rb +20 -0
- data/lib/scruffy/renderers/standard.rb +7 -6
- data/lib/scruffy/themes.rb +37 -1
- data/lib/scruffy/version.rb +1 -7
- data/spec/output/array.svg +55 -0
- data/spec/output/hash.svg +55 -0
- data/spec/scruffy/graph_spec.rb +4 -4
- data/spec/scruffy/layers/base_spec.rb +15 -10
- metadata +84 -96
- data/CHANGES +0 -104
- data/License.txt +0 -20
- data/MIT-LICENSE +0 -20
- data/Manifest.txt +0 -104
- data/PostInstall.txt +0 -6
- data/README +0 -9
- data/Rakefile +0 -108
- data/config/hoe.rb +0 -78
- data/config/requirements.rb +0 -15
- data/script/console +0 -10
- data/script/destroy +0 -14
- data/script/generate +0 -14
- data/script/txt2html +0 -82
- data/setup.rb +0 -1585
- data/spec/scruffy/layers/line_spec.rb +0 -10
- data/tasks/deployment.rake +0 -34
- data/tasks/environment.rake +0 -7
- data/tasks/website.rake +0 -17
- data/test/graph_creation_test.rb +0 -101
- data/test/test_helper.rb +0 -2
- data/website/images/blank.gif.html +0 -7
- data/website/images/graphs/all_smiles.png +0 -0
- data/website/images/graphs/bar_test.png +0 -0
- data/website/images/graphs/bar_test.svg +0 -71
- data/website/images/graphs/line_test.png +0 -0
- data/website/images/graphs/line_test.svg +0 -60
- data/website/images/graphs/multi_test.png +0 -0
- data/website/images/graphs/multi_test.svg +0 -296
- data/website/images/graphs/pie_test.png +0 -0
- data/website/images/graphs/pie_test.svg +0 -40
- data/website/images/graphs/split_test.png +0 -0
- data/website/images/graphs/split_test.svg +0 -295
- data/website/images/graphs/stacking_test.png +0 -0
- data/website/images/graphs/stacking_test.svg +0 -146
- data/website/images/header.png +0 -0
- data/website/images/header_gradient.png +0 -0
- data/website/images/overlay.png +0 -0
- data/website/images/scruffy.png +0 -0
- data/website/index.html +0 -225
- data/website/index.txt +0 -204
- data/website/javascripts/application.js +0 -2
- data/website/javascripts/controls.js +0 -815
- data/website/javascripts/dragdrop.js +0 -913
- data/website/javascripts/effects.js +0 -958
- data/website/javascripts/lightbox.js +0 -437
- data/website/javascripts/prototype.js +0 -2006
- data/website/javascripts/rounded_corners_lite.inc.js +0 -285
- data/website/stylesheets/lightbox.css +0 -27
- data/website/stylesheets/screen.css +0 -147
- data/website/stylesheets/scruffy.css +0 -227
- data/website/template.html.erb +0 -47
data/lib/scruffy/layers/base.rb
CHANGED
@@ -26,6 +26,7 @@ module Scruffy::Layers
|
|
26
26
|
attr_accessor :points
|
27
27
|
attr_accessor :relevant_data
|
28
28
|
attr_accessor :preferred_color
|
29
|
+
attr_accessor :preferred_outline
|
29
30
|
attr_accessor :options # On-the-fly values for easy customization / acts as attributes.
|
30
31
|
|
31
32
|
# The following attributes are set during the layer's render process,
|
@@ -34,6 +35,7 @@ module Scruffy::Layers
|
|
34
35
|
attr_reader :height, :width
|
35
36
|
attr_reader :min_value, :max_value
|
36
37
|
attr_reader :color
|
38
|
+
attr_reader :outline
|
37
39
|
attr_reader :opacity
|
38
40
|
attr_reader :complexity
|
39
41
|
|
@@ -47,39 +49,53 @@ module Scruffy::Layers
|
|
47
49
|
# title:: Name/title of data group
|
48
50
|
# points:: Array of data points
|
49
51
|
# preferred_color:: Color used to render this graph, overrides theme color.
|
52
|
+
# preferred_outline:: Color used to render this graph outline, overrides theme outline.
|
50
53
|
# relevant_data:: Rarely used - indicates the data on this graph should not
|
51
54
|
# included in any graph data aggregations, such as averaging data points.
|
55
|
+
# style:: SVG polyline style. (default: 'fill-opacity: 0; stroke-opacity: 0.35')
|
56
|
+
# stroke_width:: numeric value for width of line (0.1 - 10, default: 1)
|
57
|
+
# relativestroke:: stroke-width relative to image size? true or false (default)
|
58
|
+
# shadow:: Display line shadow? true or false (default)
|
59
|
+
# dots:: Display co-ord dots? true or false (default)
|
52
60
|
def initialize(options = {})
|
53
61
|
@title = options.delete(:title) || ''
|
54
|
-
@preferred_color = options.delete(:
|
62
|
+
@preferred_color = options.delete(:color)
|
63
|
+
@preferred_outline = options.delete(:outline)
|
55
64
|
@relevant_data = options.delete(:relevant_data) || true
|
56
65
|
@points = options.delete(:points) || []
|
57
66
|
@points.extend Scruffy::Helpers::PointContainer unless @points.kind_of? Scruffy::Helpers::PointContainer
|
58
|
-
|
67
|
+
|
68
|
+
options[:stroke_width] ||= 1
|
69
|
+
options[:dots] ||= false
|
70
|
+
options[:shadow] ||= false
|
71
|
+
options[:style] ||= false
|
72
|
+
options[:relativestroke] ||= false
|
73
|
+
|
59
74
|
@options = options
|
75
|
+
|
60
76
|
end
|
61
|
-
|
77
|
+
|
62
78
|
# Builds SVG code for this graph using the provided Builder object.
|
63
79
|
# This method actually generates data needed by this graph, then passes the
|
64
80
|
# rendering responsibilities to Base#draw.
|
65
81
|
#
|
66
82
|
# svg:: a Builder object used to create SVG code.
|
67
|
-
def render(svg, options
|
83
|
+
def render(svg, options)
|
68
84
|
setup_variables(options)
|
69
85
|
coords = generate_coordinates(options)
|
70
|
-
|
86
|
+
|
71
87
|
draw(svg, coords, options)
|
72
88
|
end
|
73
|
-
|
89
|
+
|
74
90
|
# The method called by Base#draw to render the graph.
|
75
|
-
#
|
91
|
+
#
|
76
92
|
# svg:: a Builder object to use for creating SVG code.
|
77
93
|
# coords:: An array of coordinates relating to the graph's data points. ie: [[100, 120], [200, 140], [300, 40]]
|
78
94
|
# options:: Optional arguments.
|
79
95
|
def draw(svg, coords, options={})
|
80
96
|
raise RenderError, "You must override the Base#draw method."
|
81
97
|
end
|
82
|
-
|
98
|
+
|
83
99
|
# Returns a hash with information to be used by the legend.
|
84
100
|
#
|
85
101
|
# Alternatively, returns nil if you don't want this layer to be in the legend,
|
@@ -90,14 +106,14 @@ module Scruffy::Layers
|
|
90
106
|
# must be rendered AFTER layers.
|
91
107
|
def legend_data
|
92
108
|
if relevant_data? && @color
|
93
|
-
{:title => title,
|
109
|
+
{:title => title,
|
94
110
|
:color => @color,
|
95
111
|
:priority => :normal}
|
96
112
|
else
|
97
113
|
nil
|
98
114
|
end
|
99
115
|
end
|
100
|
-
|
116
|
+
|
101
117
|
# Returns the value of relevant_data
|
102
118
|
def relevant_data?
|
103
119
|
@relevant_data
|
@@ -107,12 +123,22 @@ module Scruffy::Layers
|
|
107
123
|
def top_value
|
108
124
|
@relevant_data ? points.maximum_value : nil
|
109
125
|
end
|
110
|
-
|
126
|
+
|
111
127
|
# The lowest data point on this layer, or nil if relevant_data == false
|
112
128
|
def bottom_value
|
113
129
|
@relevant_data ? points.minimum_value : nil
|
114
130
|
end
|
115
131
|
|
132
|
+
# The highest data point on this layer, or nil if relevant_data == false
|
133
|
+
def bottom_key
|
134
|
+
@relevant_data ? points.minimum_key : nil
|
135
|
+
end
|
136
|
+
|
137
|
+
# The lowest data point on this layer, or nil if relevant_data == false
|
138
|
+
def top_key
|
139
|
+
@relevant_data ? points.maximum_key : nil
|
140
|
+
end
|
141
|
+
|
116
142
|
# The sum of all values
|
117
143
|
def sum_values
|
118
144
|
points.sum
|
@@ -123,6 +149,7 @@ module Scruffy::Layers
|
|
123
149
|
# itself.
|
124
150
|
def setup_variables(options = {})
|
125
151
|
@color = (preferred_color || options.delete(:color))
|
152
|
+
@outline = (preferred_outline || options.delete(:outline))
|
126
153
|
@width, @height = options.delete(:size)
|
127
154
|
@min_value, @max_value = options[:min_value], options[:max_value]
|
128
155
|
@opacity = options[:opacity] || 1.0
|
@@ -132,21 +159,24 @@ module Scruffy::Layers
|
|
132
159
|
# Optimistic generation of coordinates for layer to use. These coordinates are
|
133
160
|
# just a best guess, and can be overridden or thrown away (for example, this is overridden
|
134
161
|
# in pie charting and bar charts).
|
162
|
+
|
163
|
+
# Updated : Assuming n number of points, the graph is divided into n rectangles
|
164
|
+
# and the points are plotted in the middle of each rectangle. This allows bars to
|
165
|
+
# play nice with lines.
|
135
166
|
def generate_coordinates(options = {})
|
136
|
-
|
167
|
+
dy = height.to_f / (options[:max_value] - options[:min_value])
|
168
|
+
dx = width.to_f / (options[:max_key] - options[:min_key] + 1)
|
137
169
|
|
138
|
-
|
139
|
-
|
170
|
+
ret = []
|
171
|
+
points.each_point do |x, y|
|
172
|
+
if y
|
173
|
+
x_coord = dx * (x - options[:min_key]) + dx/2
|
174
|
+
y_coord = dy * (y - options[:min_value])
|
140
175
|
|
141
|
-
|
142
|
-
relative_percent = ((point == min_value) ? 0 : ((point - min_value) / (max_value - min_value).to_f))
|
143
|
-
y_coord = (height - (height * relative_percent))
|
144
|
-
|
145
|
-
memo << [x_coord, y_coord]
|
176
|
+
ret << [x_coord, height - y_coord]
|
146
177
|
end
|
147
|
-
|
148
|
-
memo
|
149
178
|
end
|
179
|
+
return ret
|
150
180
|
end
|
151
181
|
|
152
182
|
# Converts a percentage into a pixel value, relative to the height.
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module Scruffy::Layers
|
2
|
+
# ==Scruffy::Layers::Box
|
3
|
+
#
|
4
|
+
# Author:: Brasten Sager
|
5
|
+
# Date:: August 6th, 2006
|
6
|
+
#
|
7
|
+
# Standard bar graph.
|
8
|
+
class Box < Base
|
9
|
+
|
10
|
+
# Draw box plot.
|
11
|
+
def draw(svg, coords, options = {})
|
12
|
+
coords.each_with_index do |coord,idx|
|
13
|
+
x, y, bar_height = (coord.first), coord.last, 1#(height - coord.last)
|
14
|
+
|
15
|
+
valh = max_value + min_value * -1 #value_height
|
16
|
+
maxh = max_value * height / valh #positive area height
|
17
|
+
minh = min_value * height / valh #negative area height
|
18
|
+
#puts "height = #{height} and max_value = #{max_value} and min_value = #{min_value} and y = #{y} and point = #{points[idx]}"
|
19
|
+
|
20
|
+
#if points[idx] > 0
|
21
|
+
# bar_height = points[idx]*maxh/max_value
|
22
|
+
#else
|
23
|
+
# bar_height = points[idx]*minh/min_value
|
24
|
+
#end
|
25
|
+
|
26
|
+
#puts " y = #{y} and point = #{points[idx]}"
|
27
|
+
#svg.g(:transform => "translate(-#{relative(0.5)}, -#{relative(0.5)})") {
|
28
|
+
# svg.rect( :x => x, :y => y, :width => @bar_width + relative(1), :height => bar_height + relative(1),
|
29
|
+
# :style => "fill: black; fill-opacity: 0.15; stroke: none;" )
|
30
|
+
# svg.rect( :x => x+relative(0.5), :y => y+relative(2), :width => @bar_width + relative(1), :height => bar_height - relative(0.5),
|
31
|
+
# :style => "fill: black; fill-opacity: 0.15; stroke: none;" )
|
32
|
+
#
|
33
|
+
#}
|
34
|
+
|
35
|
+
svg.line(:x1=>x+@bar_width/2,:x2=>x+@bar_width/2,:y1=>y[0],:y2=>y[4], :style => "stroke:#{(outline.to_s || options[:theme].marker || 'white').to_s}; stroke-width:1")
|
36
|
+
svg.line(:x1=>x+@bar_width/4,:x2=>x+@bar_width/4*3,:y1=>y[0],:y2=>y[0], :style => "stroke:#{(outline.to_s || options[:theme].marker || 'white').to_s}; stroke-width:1")
|
37
|
+
svg.line(:x1=>x+@bar_width/4,:x2=>x+@bar_width/4*3,:y1=>y[4],:y2=>y[4], :style => "stroke:#{(outline.to_s || options[:theme].marker || 'white').to_s}; stroke-width:1")
|
38
|
+
svg.rect( :x => x, :y => y[1], :width => @bar_width, :height => (y[1]-y[3])*-1,
|
39
|
+
:fill => color.to_s, 'style' => "opacity: #{opacity}; stroke:#{(outline.to_s || options[:theme].marker || 'white').to_s}; stroke-width:1;" )
|
40
|
+
svg.line(:x1=>x,:x2=>x+@bar_width,:y1=>y[2],:y2=>y[2], :style => "stroke:#{(outline.to_s || options[:theme].marker || 'white').to_s}; stroke-width:1")
|
41
|
+
#svg.rect( :x => x, :y => y, :width => @bar_width, :height => bar_height,
|
42
|
+
# :fill => color.to_s, 'style' => "opacity: #{opacity}; stroke: none;" )
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
# Returns the highest value in any of this container's layers.
|
48
|
+
#
|
49
|
+
# If padding is set to :padded, a 15% padding is added to the highest value.
|
50
|
+
def top_value(padding=nil) # :nodoc:
|
51
|
+
topval = points[0].max
|
52
|
+
points.each do |point_set|
|
53
|
+
topval = ( (topval < point_set.max) ? point_set.max : topval )
|
54
|
+
end
|
55
|
+
#topval = layers.inject(0) { |max, layer| (max = ((max < layer.top_value) ? layer.top_value : max)) unless layer.top_value.nil?; max }
|
56
|
+
below_zero = (topval <= 0)
|
57
|
+
topval = padding == :padded ? (topval + ((topval - bottom_value(nil)) * 0.15)) : topval
|
58
|
+
(below_zero && topval > 0) ? 0 : topval
|
59
|
+
end
|
60
|
+
|
61
|
+
# Returns the lowest value in any of this container's layers.
|
62
|
+
#
|
63
|
+
# If padding is set to :padded, a 15% padding is added below the lowest value.
|
64
|
+
# If the lowest value is greater than zero, then the padding will not cross the zero line, preventing
|
65
|
+
# negative values from being introduced into the graph purely due to padding.
|
66
|
+
def bottom_value(padding=nil) # :nodoc:
|
67
|
+
botval = points[0].min
|
68
|
+
points.each do |point_set|
|
69
|
+
botval = ( (botval>point_set.min) ? point_set.min : botval )
|
70
|
+
end
|
71
|
+
#botval = layers.inject(0) do |min, layer|
|
72
|
+
# (min = ((min > layer.bottom_value) ? layer.bottom_value : min)) unless layer.bottom_value.nil?
|
73
|
+
# min
|
74
|
+
#end
|
75
|
+
above_zero = (botval >= 0)
|
76
|
+
botval = (botval - ((top_value(nil) - botval) * 0.15)) if padding == :padded
|
77
|
+
|
78
|
+
# Don't introduce negative values solely due to padding.
|
79
|
+
# A user-provided value must be negative before padding will extend into negative values.
|
80
|
+
(above_zero && botval < 0) ? 0 : botval
|
81
|
+
end
|
82
|
+
|
83
|
+
protected
|
84
|
+
|
85
|
+
# Due to the size of the bar graph, X-axis coords must
|
86
|
+
# be squeezed so that the bars do not hang off the ends
|
87
|
+
# of the graph.
|
88
|
+
#
|
89
|
+
# Unfortunately this just mean that bar-graphs and most other graphs
|
90
|
+
# end up on different points. Maybe adding a padding to the coordinates
|
91
|
+
# should be a graph-wide thing?
|
92
|
+
#
|
93
|
+
# Update : x-axis coords for lines and area charts should now line
|
94
|
+
# up with the center of bar charts.
|
95
|
+
|
96
|
+
def generate_coordinates(options = {})
|
97
|
+
@bar_width = (width / points.size) * 0.9
|
98
|
+
options[:point_distance] = (width - (width / points.size)) / (points.size - 1).to_f
|
99
|
+
|
100
|
+
#TODO more array work with index, try to rework to be accepting of hashes
|
101
|
+
coords = (0...points.size).map do |idx|
|
102
|
+
x_coord = (options[:point_distance] * idx) + (width / points.size * 0.5) - (@bar_width * 0.5)
|
103
|
+
y_coords = []
|
104
|
+
points[idx].each do |point|
|
105
|
+
relative_percent = ((point == min_value) ? 0 : ((point - min_value) / (max_value - min_value).to_f))
|
106
|
+
y_coord = (height - (height * relative_percent))
|
107
|
+
y_coords << y_coord
|
108
|
+
end
|
109
|
+
[x_coord, y_coords]
|
110
|
+
end
|
111
|
+
coords
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
data/lib/scruffy/layers/line.rb
CHANGED
@@ -8,22 +8,39 @@ module Scruffy::Layers
|
|
8
8
|
class Line < Base
|
9
9
|
|
10
10
|
# Renders line graph.
|
11
|
+
#
|
12
|
+
# Options:
|
13
|
+
# See initialize()
|
11
14
|
def draw(svg, coords, options={})
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
15
|
+
|
16
|
+
# Include options provided when the object was created
|
17
|
+
options.merge!(@options)
|
18
|
+
|
19
|
+
stroke_width = (options[:relativestroke]) ? relative(options[:stroke_width]) : options[:stroke_width]
|
20
|
+
style = (options[:style]) ? options[:style] : ''
|
21
|
+
|
22
|
+
if options[:shadow]
|
23
|
+
svg.g(:class => 'shadow', :transform => "translate(#{relative(0.5)}, #{relative(0.5)})") {
|
24
|
+
svg.polyline( :points => stringify_coords(coords).join(' '), :fill => 'transparent',
|
25
|
+
:stroke => 'black', 'stroke-width' => stroke_width,
|
26
|
+
:style => 'fill-opacity: 0; stroke-opacity: 0.35' )
|
27
|
+
|
28
|
+
if options[:dots]
|
29
|
+
coords.each { |coord| svg.circle( :cx => coord.first, :cy => coord.last + relative(0.9), :r => stroke_width,
|
30
|
+
:style => "stroke-width: #{stroke_width}; stroke: black; opacity: 0.35;" ) }
|
31
|
+
end
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
24
35
|
|
25
|
-
|
26
|
-
|
36
|
+
svg.polyline( :points => stringify_coords(coords).join(' '), :fill => 'none', :stroke => @color.to_s,
|
37
|
+
'stroke-width' => stroke_width, :style => style )
|
38
|
+
|
39
|
+
if options[:dots]
|
40
|
+
coords.each { |coord| svg.circle( :cx => coord.first, :cy => coord.last, :r => stroke_width,
|
41
|
+
:style => "stroke-width: #{stroke_width}; stroke: #{color.to_s}; fill: #{color.to_s}" ) }
|
42
|
+
end
|
43
|
+
|
27
44
|
end
|
28
45
|
end
|
29
46
|
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Scruffy::Layers
|
2
|
+
# ==Scruffy::Layers::Multi
|
3
|
+
#
|
4
|
+
# Author:: Jeremy Green
|
5
|
+
# Date:: July 29th, 2009
|
6
|
+
#
|
7
|
+
# Based on:: Scruffy::Layers::Stacked by
|
8
|
+
# Author:: Brasten Sager
|
9
|
+
# Date:: August 12th, 2006
|
10
|
+
#
|
11
|
+
# Provides a generic way for displaying multiple bar graphs side by side.
|
12
|
+
class Multi < Base
|
13
|
+
include Scruffy::Helpers::LayerContainer
|
14
|
+
|
15
|
+
# Returns new Multi graph.
|
16
|
+
#
|
17
|
+
# You can provide a block for easily adding layers during (just after) initialization.
|
18
|
+
# Example:
|
19
|
+
# Multi.new do |multi|
|
20
|
+
# multi << Scruffy::Layers::Line.new( ... )
|
21
|
+
# multi.add(:multi_bar, 'My Bar', [...])
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# The initialize method passes itself to the block, and since multi is a LayerContainer,
|
25
|
+
# layers can be added just as if they were being added to Graph.
|
26
|
+
def initialize(options={}, &block)
|
27
|
+
super(options)
|
28
|
+
|
29
|
+
block.call(self) # Allow for population of data with a block during initialization.
|
30
|
+
end
|
31
|
+
|
32
|
+
# Overrides Base#render to fiddle with layers' points to achieve a multi effect.
|
33
|
+
def render(svg, options = {})
|
34
|
+
#TODO ensure this works with new points
|
35
|
+
#current_points = points
|
36
|
+
layers.each_with_index do |layer,i|
|
37
|
+
|
38
|
+
#real_points = layer.points
|
39
|
+
#layer.points = current_points
|
40
|
+
layer_options = options.dup
|
41
|
+
|
42
|
+
layer_options[:num_bars] = layers.size
|
43
|
+
layer_options[:position] = i
|
44
|
+
layer_options[:color] = layer.preferred_color || layer.color || options[:theme].next_color
|
45
|
+
layer.render(svg, layer_options)
|
46
|
+
|
47
|
+
options.merge(layer_options)
|
48
|
+
|
49
|
+
#layer.points = real_points
|
50
|
+
#layer.points.each_with_index { |val, idx| current_points[idx] -= val }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# A multi graph has many data sets. Return legend information for all of them.
|
55
|
+
def legend_data
|
56
|
+
if relevant_data?
|
57
|
+
retval = []
|
58
|
+
layers.each do |layer|
|
59
|
+
retval << layer.legend_data
|
60
|
+
end
|
61
|
+
retval
|
62
|
+
else
|
63
|
+
nil
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# TODO, special points accessor
|
68
|
+
|
69
|
+
|
70
|
+
def points=(val)
|
71
|
+
throw ArgumentsError, "Multi layers cannot accept points, only other layers."
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
module Scruffy::Layers
|
2
|
+
# ==Scruffy::Layers::MulitArea
|
3
|
+
#
|
4
|
+
# Author:: Martyn Taylor
|
5
|
+
# Date:: July 30th 2010
|
6
|
+
#
|
7
|
+
# Multi Area graph.
|
8
|
+
|
9
|
+
class MultiArea < Base
|
10
|
+
|
11
|
+
attr_accessor :baselines
|
12
|
+
attr_accessor :area_colors
|
13
|
+
|
14
|
+
def initialize(options = {}, &block)
|
15
|
+
super(options)
|
16
|
+
@area_colors = options[:area_colors] ? options[:area_colors] : nil
|
17
|
+
@baselines = options[:baselines] ? options[:baselines] : nil
|
18
|
+
end
|
19
|
+
|
20
|
+
# Render Multi Area graph.
|
21
|
+
def draw(svg, coords, options={})
|
22
|
+
# Check whether to use color from theme, or whether to use user defined colors from the area_colors array
|
23
|
+
color_count = nil
|
24
|
+
if @area_colors && @area_colors.size > 0
|
25
|
+
area_color = @area_colors[0]
|
26
|
+
color_count = 1
|
27
|
+
else
|
28
|
+
puts "Never Set Area Color"
|
29
|
+
area_color = color
|
30
|
+
end
|
31
|
+
|
32
|
+
# Draw Bottom Level Polygons (Original Coords)
|
33
|
+
draw_poly(svg, coords, area_color, options = {})
|
34
|
+
|
35
|
+
# Draw Lower Area Polygons
|
36
|
+
if @baselines
|
37
|
+
# Get the Color of this Area
|
38
|
+
puts "Drawing Baselines"
|
39
|
+
@baselines.sort! {|x,y| y <=> x }
|
40
|
+
@baselines.each do |baseline|
|
41
|
+
if color_count
|
42
|
+
area_color = area_colors[color_count]
|
43
|
+
color_count = color_count + 1
|
44
|
+
puts area_color.to_s
|
45
|
+
if color_count >= area_colors.size
|
46
|
+
color_count = 0
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
lower_poly_coords = create_lower_polygon_coords(translate_number(baseline), coords, options)
|
51
|
+
draw_poly(svg, lower_poly_coords, area_color, options = {})
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
def draw_poly(svg, coords, color, options={})
|
58
|
+
points_value1 = "0,#{height} #{stringify_coords(coords).join(' ')} #{width},#{height}"
|
59
|
+
svg.polygon(:points => points_value1, :fill => color.to_s, :stroke => color.to_s, 'style' => "opacity: #{opacity}")
|
60
|
+
end
|
61
|
+
|
62
|
+
def calculate_intersection(baseline, point1, point2)
|
63
|
+
x1 = point1[0]
|
64
|
+
y1 = point1[1]
|
65
|
+
x2 = point2[0]
|
66
|
+
y2 = point2[1]
|
67
|
+
|
68
|
+
# Check whether baseline intersects with the two point line from these coords
|
69
|
+
if ((y2 >= baseline) && (y1 <= baseline)) || ((y1 >= baseline) && (y2 <= baseline))
|
70
|
+
# Calculate point of intersection
|
71
|
+
y = baseline.to_f
|
72
|
+
m = (y2.to_f - y1.to_f) / (x2.to_f - x1.to_f)
|
73
|
+
c = y1.to_f
|
74
|
+
|
75
|
+
x = (y.to_f - c.to_f) / m.to_f
|
76
|
+
return [x + x1, y]
|
77
|
+
elsif (y2 <= baseline) && (y1 <= baseline)
|
78
|
+
return nil
|
79
|
+
else
|
80
|
+
return [x2, y2]
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
def create_lower_polygon_coords(baseline, coords, options={})
|
86
|
+
lower_poly_coords = []
|
87
|
+
if coords[0][1] <= baseline
|
88
|
+
lower_poly_coords << [coords[0][0], baseline]
|
89
|
+
else
|
90
|
+
lower_poly_coords << coords[0]
|
91
|
+
end
|
92
|
+
|
93
|
+
coords.each_index do |index|
|
94
|
+
if index + 1 < coords.size
|
95
|
+
poi = calculate_intersection(baseline, coords[index], coords[index + 1])
|
96
|
+
if poi
|
97
|
+
lower_poly_coords << poi
|
98
|
+
if(coords[index + 1][1] > baseline)
|
99
|
+
lower_poly_coords << coords[index + 1]
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
if coords.last[1] <= baseline
|
106
|
+
lower_poly_coords << [coords.last[0], baseline]
|
107
|
+
else
|
108
|
+
lower_poly_coords << coords.last
|
109
|
+
end
|
110
|
+
|
111
|
+
return lower_poly_coords
|
112
|
+
end
|
113
|
+
|
114
|
+
def translate_number(baseline)
|
115
|
+
relative_percent = ((baseline == min_value) ? 0 : ((baseline.to_f - min_value.to_f) / (max_value.to_f - min_value.to_f).to_f))
|
116
|
+
return (height.to_f - (height.to_f * relative_percent))
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|