scruffy 0.2.6 → 0.3.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|