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/helpers.rb
CHANGED
@@ -1,2 +1,10 @@
|
|
1
|
+
# ==Scruffy Helpers
|
2
|
+
#
|
3
|
+
# Author:: Brasten Sager
|
4
|
+
# Date:: August 16th, 2006
|
5
|
+
#
|
6
|
+
# Modules which provide helper methods for various situations.
|
7
|
+
module Scruffy::Helpers; end
|
8
|
+
|
1
9
|
require 'scruffy/helpers/canvas'
|
2
10
|
require 'scruffy/helpers/layer_container'
|
@@ -1,22 +1,25 @@
|
|
1
|
-
module Scruffy
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
end
|
1
|
+
module Scruffy::Helpers
|
2
|
+
# ==Scruffy::Helpers::Canvas
|
3
|
+
#
|
4
|
+
# Author:: Brasten Sager
|
5
|
+
# Date:: August 16th, 2006
|
6
|
+
#
|
7
|
+
# Provides common methods for canvas objects. Primarily used for providing spacial-type calculations
|
8
|
+
# where necessary.
|
9
|
+
module Canvas
|
10
|
+
protected
|
11
|
+
# Converts percentage values into actual pixe values based on the known render size.
|
12
|
+
#
|
13
|
+
# Returns a hash consisting of :x, :y, :width, and :height elements.
|
14
|
+
def bounds_for(canvas_size, position, size)
|
15
|
+
return nil if (position.nil? || size.nil?)
|
16
|
+
bounds = {}
|
17
|
+
bounds[:x] = canvas_size.first * (position.first / 100.to_f)
|
18
|
+
bounds[:y] = canvas_size.last * (position.last / 100.to_f)
|
19
|
+
bounds[:width] = canvas_size.first * (size.first / 100.to_f)
|
20
|
+
bounds[:height] = canvas_size.last * (size.last / 100.to_f)
|
21
|
+
bounds
|
22
|
+
end
|
23
|
+
end # canvas
|
24
|
+
|
25
|
+
end # scruffy::helpers
|
@@ -1,4 +1,12 @@
|
|
1
1
|
module Scruffy::Helpers
|
2
|
+
# ==Scruffy::Helpers::LayerContainer
|
3
|
+
#
|
4
|
+
# Author:: Brasten Sager
|
5
|
+
# Date:: August 16th, 2006
|
6
|
+
#
|
7
|
+
# Adds some common functionality to any object which needs to act as a
|
8
|
+
# container for graph layers. The best example of this is the Scruffy::Graph
|
9
|
+
# object itself, but this module is also used by Scruffy::Layer::Stacked.
|
2
10
|
module LayerContainer
|
3
11
|
# Adds a Layer to the Graph/Container. Accepts either a list of arguments used to build a new layer, or
|
4
12
|
# a Scruffy::Layers::Base-derived object. When passing a list of arguments, all arguments are optional, but the arguments
|
@@ -32,23 +40,33 @@ module Scruffy::Helpers
|
|
32
40
|
end
|
33
41
|
layer
|
34
42
|
end
|
35
|
-
|
43
|
+
|
36
44
|
alias :add :<<
|
37
45
|
|
38
46
|
|
39
|
-
#
|
47
|
+
# Layer Writer
|
40
48
|
def layers=(val)
|
41
49
|
@layers = val
|
42
50
|
end
|
51
|
+
|
52
|
+
# Layer Reader
|
43
53
|
def layers
|
44
54
|
@layers ||= []
|
45
55
|
end
|
46
56
|
|
57
|
+
# Returns the highest value in any of this container's layers.
|
58
|
+
#
|
59
|
+
# If padding is set to :padded, a 15% padding is added to the highest value.
|
47
60
|
def top_value(padding=nil) # :nodoc:
|
48
61
|
topval = layers.inject(0) { |max, layer| (max = ((max < layer.top_value) ? layer.top_value : max)) unless layer.top_value.nil?; max }
|
49
62
|
padding == :padded ? (topval - ((topval - bottom_value) * 0.15)) : topval
|
50
63
|
end
|
51
64
|
|
65
|
+
# Returns the lowest value in any of this container's layers.
|
66
|
+
#
|
67
|
+
# If padding is set to :padded, a 15% padding is added below the lowest value.
|
68
|
+
# If the lowest value is greater than zero, then the padding will not cross the zero line, preventing
|
69
|
+
# negative values from being introduced into the graph purely due to padding.
|
52
70
|
def bottom_value(padding=nil) # :nodoc:
|
53
71
|
botval = layers.inject(top_value) { |min, layer| (min = ((min > layer.bottom_value) ? layer.bottom_value : min)) unless layer.bottom_value.nil?; min }
|
54
72
|
above_zero = (botval > 0)
|
data/lib/scruffy/layers.rb
CHANGED
@@ -1,4 +1,18 @@
|
|
1
|
-
#
|
1
|
+
# ==Scruffy::Layers
|
2
|
+
#
|
3
|
+
# Author:: Brasten Sager
|
4
|
+
# Date:: August 10th, 2006
|
5
|
+
#
|
6
|
+
# See documentation in Scruffy::Layers::Base
|
7
|
+
#
|
8
|
+
module Scruffy::Layers
|
9
|
+
|
10
|
+
# Should be raised whenever a predictable error during rendering occurs,
|
11
|
+
# particularly if you do not want to terminate the graph rendering process.
|
12
|
+
class RenderError < StandardError; end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
2
16
|
require 'scruffy/layers/base'
|
3
17
|
require 'scruffy/layers/area'
|
4
18
|
require 'scruffy/layers/all_smiles'
|
@@ -1,113 +1,137 @@
|
|
1
|
-
module Scruffy
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
module Scruffy::Layers
|
2
|
+
# ==Scruffy::Layers::AllSmiles
|
3
|
+
#
|
4
|
+
# Author:: Brasten Sager
|
5
|
+
# Date:: August 8th, 2006
|
6
|
+
#
|
7
|
+
# The AllSmiles graph consists of smiley faces for data points, with smiles or frowns depending upon
|
8
|
+
# their relative location on the graph. The highest point is crowned with a wizard hat. The Wizard
|
9
|
+
# Smiley eventually become 'Scruffy', our mascot.
|
10
|
+
#
|
11
|
+
# I don't know why.
|
12
|
+
#
|
13
|
+
# This graph only looks decent in SVG mode. If you're rasterizing the graph with ImageMagick, you
|
14
|
+
# must use the :complexity => :minimal option on Graph#render. This will make the graph look really
|
15
|
+
# nasty, but still better than if you try to rasterize with all the gradients in place.
|
16
|
+
class AllSmiles < Base
|
17
|
+
attr_accessor :standalone
|
18
|
+
|
19
|
+
# Returns a new AllSmiles graph.
|
20
|
+
#
|
21
|
+
# Options:
|
22
|
+
# standalone:: If set to true, dashed lines under smilies run vertically, like bar graphs.
|
23
|
+
# If false (default), dashed lines run from smiley to smiley, like a line-graph.
|
24
|
+
def initialize(options = {})
|
25
|
+
super
|
26
|
+
@standalone = options[:standalone] || false
|
27
|
+
end
|
28
|
+
|
29
|
+
# Renders graph.
|
30
|
+
def draw(svg, coords, options={})
|
5
31
|
|
6
|
-
|
7
|
-
|
8
|
-
@standalone = options[:standalone] || false
|
9
|
-
end
|
32
|
+
hero_smiley = nil
|
33
|
+
coords.each { |c| hero_smiley = c.last if (hero_smiley.nil? || c.last < hero_smiley) }
|
10
34
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
svg.stop(:offset => '0%', 'stop-color' => '#FEE')
|
31
|
-
svg.stop(:offset => '20%', 'stop-color' => '#F0E0C0')
|
32
|
-
svg.stop(:offset => '45%', 'stop-color' => '#8A2A1A')
|
33
|
-
svg.stop(:offset => '60%', 'stop-color' => '#821')
|
34
|
-
svg.stop(:offset => '90%', 'stop-color' => '#210')
|
35
|
-
}
|
36
|
-
svg.radialGradient(:id => 'StarGradient', :cx => '50%',
|
37
|
-
:cy => '50%', :r => '50%', :fx => '30%', :fy => '30%') {
|
38
|
-
|
39
|
-
svg.stop(:offset => '0%', 'stop-color' => '#FFF')
|
40
|
-
svg.stop(:offset => '20%', 'stop-color' => '#EFEFEF')
|
41
|
-
svg.stop(:offset => '45%', 'stop-color' => '#DDD')
|
42
|
-
svg.stop(:offset => '60%', 'stop-color' => '#BBB')
|
43
|
-
svg.stop(:offset => '90%', 'stop-color' => '#888')
|
44
|
-
}
|
35
|
+
svg.defs {
|
36
|
+
svg.radialGradient(:id => 'SmileyGradient', :cx => '50%',
|
37
|
+
:cy => '50%', :r => '50%', :fx => '30%', :fy => '30%') {
|
38
|
+
|
39
|
+
svg.stop(:offset => '0%', 'stop-color' => '#FFF')
|
40
|
+
svg.stop(:offset => '20%', 'stop-color' => '#FFC')
|
41
|
+
svg.stop(:offset => '45%', 'stop-color' => '#FF3')
|
42
|
+
svg.stop(:offset => '60%', 'stop-color' => '#FF0')
|
43
|
+
svg.stop(:offset => '90%', 'stop-color' => '#990')
|
44
|
+
svg.stop(:offset => '100%', 'stop-color' => '#220')
|
45
|
+
}
|
46
|
+
svg.radialGradient(:id => 'HeroGradient', :cx => '50%',
|
47
|
+
:cy => '50%', :r => '50%', :fx => '30%', :fy => '30%') {
|
48
|
+
|
49
|
+
svg.stop(:offset => '0%', 'stop-color' => '#FEE')
|
50
|
+
svg.stop(:offset => '20%', 'stop-color' => '#F0E0C0')
|
51
|
+
svg.stop(:offset => '45%', 'stop-color' => '#8A2A1A')
|
52
|
+
svg.stop(:offset => '60%', 'stop-color' => '#821')
|
53
|
+
svg.stop(:offset => '90%', 'stop-color' => '#210')
|
45
54
|
}
|
46
|
-
|
47
|
-
|
48
|
-
|
55
|
+
svg.radialGradient(:id => 'StarGradient', :cx => '50%',
|
56
|
+
:cy => '50%', :r => '50%', :fx => '30%', :fy => '30%') {
|
57
|
+
|
58
|
+
svg.stop(:offset => '0%', 'stop-color' => '#FFF')
|
59
|
+
svg.stop(:offset => '20%', 'stop-color' => '#EFEFEF')
|
60
|
+
svg.stop(:offset => '45%', 'stop-color' => '#DDD')
|
61
|
+
svg.stop(:offset => '60%', 'stop-color' => '#BBB')
|
62
|
+
svg.stop(:offset => '90%', 'stop-color' => '#888')
|
63
|
+
}
|
64
|
+
}
|
65
|
+
|
66
|
+
unless standalone
|
67
|
+
svg.polyline( :points => stringify_coords(coords).join(' '), :fill => 'none',
|
68
|
+
:stroke => '#660', 'stroke-width' => scaled(10), 'stroke-dasharray' => "#{scaled(10)}, #{scaled(10)}" )
|
69
|
+
end
|
70
|
+
|
71
|
+
# Draw smilies.
|
72
|
+
coords.each do |coord|
|
73
|
+
if standalone
|
74
|
+
svg.line( :x1 => coord.first, :y1 => coord.last, :x2 => coord.first, :y2 => height, :fill => 'none',
|
49
75
|
:stroke => '#660', 'stroke-width' => scaled(10), 'stroke-dasharray' => "#{scaled(10)}, #{scaled(10)}" )
|
50
76
|
end
|
77
|
+
svg.circle( :cx => coord.first + scaled(2), :cy => coord.last + scaled(2), :r => scaled(15),
|
78
|
+
:fill => 'black', :stroke => 'none', :opacity => 0.4)
|
79
|
+
svg.circle( :cx => coord.first, :cy => coord.last, :r => scaled(15),
|
80
|
+
:fill => (complexity == :minimal ? 'yellow' : 'url(#SmileyGradient)'), :stroke => 'black', 'stroke-width' => scaled(1) )
|
81
|
+
svg.line( :x1 => (coord.first - scaled(3)),
|
82
|
+
:x2 => (coord.first - scaled(3)),
|
83
|
+
:y1 => (coord.last),
|
84
|
+
:y2 => (coord.last - scaled(7)), :stroke => 'black', 'stroke-width' => scaled(1.4) )
|
85
|
+
svg.line( :x1 => (coord.first + scaled(3)),
|
86
|
+
:x2 => (coord.first + scaled(3)),
|
87
|
+
:y1 => (coord.last),
|
88
|
+
:y2 => (coord.last - scaled(7)), :stroke => 'black', 'stroke-width' => scaled(1.4) )
|
89
|
+
|
51
90
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
end
|
57
|
-
svg.circle( :cx => coord.first + scaled(2), :cy => coord.last + scaled(2), :r => scaled(15),
|
58
|
-
:fill => 'black', :stroke => 'none', :opacity => 0.4)
|
59
|
-
svg.circle( :cx => coord.first, :cy => coord.last, :r => scaled(15),
|
60
|
-
:fill => (complexity == :minimal ? 'yellow' : 'url(#SmileyGradient)'), :stroke => 'black', 'stroke-width' => scaled(1) )
|
61
|
-
svg.line( :x1 => (coord.first - scaled(3)),
|
62
|
-
:x2 => (coord.first - scaled(3)),
|
63
|
-
:y1 => (coord.last),
|
64
|
-
:y2 => (coord.last - scaled(7)), :stroke => 'black', 'stroke-width' => scaled(1.4) )
|
65
|
-
svg.line( :x1 => (coord.first + scaled(3)),
|
66
|
-
:x2 => (coord.first + scaled(3)),
|
67
|
-
:y1 => (coord.last),
|
68
|
-
:y2 => (coord.last - scaled(7)), :stroke => 'black', 'stroke-width' => scaled(1.4) )
|
69
|
-
|
91
|
+
# Some minor mathematics for the smile/frown
|
92
|
+
percent = 1.0 - (coord.last.to_f / height.to_f)
|
93
|
+
corners = scaled(8 - (5 * percent))
|
94
|
+
anchor = scaled((20 * percent) - 5)
|
70
95
|
|
71
|
-
|
96
|
+
# Draw the mouth
|
97
|
+
svg.path( :d => "M#{coord.first - scaled(9)} #{coord.last + corners} Q#{coord.first} #{coord.last + anchor} #{coord.first + scaled(9)} #{coord.last + corners}",
|
98
|
+
:stroke => 'black', 'stroke-width' => scaled(1.4), :fill => 'none' )
|
99
|
+
|
100
|
+
|
101
|
+
# Wizard hat for hero smiley.
|
102
|
+
if coord.last == hero_smiley
|
103
|
+
svg.ellipse(:cx => coord.first, :cy => (coord.last - scaled(13)),
|
104
|
+
:rx => scaled(17), :ry => scaled(6.5), :fill => (complexity == :minimal ? 'purple' : 'url(#HeroGradient)'), :stroke => 'black', 'stroke-width' => scaled(1.4) )
|
72
105
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
# top hat
|
80
|
-
if coord.last == hero_smiley
|
81
|
-
svg.ellipse(:cx => coord.first, :cy => (coord.last - scaled(13)),
|
82
|
-
:rx => scaled(17), :ry => scaled(6.5), :fill => (complexity == :minimal ? 'purple' : 'url(#HeroGradient)'), :stroke => 'black', 'stroke-width' => scaled(1.4) )
|
83
|
-
|
84
|
-
svg.path(:d => "M#{coord.first} #{coord.last - scaled(60)} " +
|
85
|
-
"L#{coord.first + scaled(10)} #{coord.last - scaled(14)} " +
|
86
|
-
"C#{coord.first + scaled(10)},#{coord.last - scaled(9)} #{coord.first - scaled(10)},#{coord.last - scaled(9)} #{coord.first - scaled(10)},#{coord.last - scaled(14)}" +
|
87
|
-
"L#{coord.first} #{coord.last - scaled(60)}",
|
88
|
-
:stroke => 'black', 'stroke-width' => scaled(1.4), :fill => (complexity == :minimal ? 'purple' : 'url(#HeroGradient)'))
|
89
|
-
|
90
|
-
svg.path(:d => "M#{coord.first - scaled(4)} #{coord.last - scaled(23)}" +
|
91
|
-
"l-#{scaled(2.5)} #{scaled(10)} l#{scaled(7.5)} -#{scaled(5)} l-#{scaled(10)} 0 l#{scaled(7.5)} #{scaled(5)} l-#{scaled(2.5)} -#{scaled(10)}", :stroke => 'none', :fill => (complexity == :minimal ? 'white': 'url(#StarGradient)') )
|
92
|
-
svg.path(:d => "M#{coord.first + scaled(2)} #{coord.last - scaled(30)}" +
|
93
|
-
"l-#{scaled(2.5)} #{scaled(10)} l#{scaled(7.5)} -#{scaled(5)} l-#{scaled(10)} 0 l#{scaled(7.5)} #{scaled(5)} l-#{scaled(2.5)} -#{scaled(10)}", :stroke => 'none', :fill => (complexity == :minimal ? 'white': 'url(#StarGradient)') )
|
94
|
-
svg.path(:d => "M#{coord.first - scaled(2)} #{coord.last - scaled(33)}" +
|
95
|
-
"l-#{scaled(1.25)} #{scaled(5)} l#{scaled(3.75)} -#{scaled(2.5)} l-#{scaled(5)} 0 l#{scaled(3.75)} #{scaled(2.5)} l-#{scaled(1.25)} -#{scaled(5)}", :stroke => 'none', :fill => 'white' )
|
96
|
-
svg.path(:d => "M#{coord.first - scaled(2.2)} #{coord.last - scaled(32.7)}" +
|
97
|
-
"l-#{scaled(1.25)} #{scaled(5)} l#{scaled(3.75)} -#{scaled(2.5)} l-#{scaled(5)} 0 l#{scaled(3.75)} #{scaled(2.5)} l-#{scaled(1.25)} -#{scaled(5)}", :stroke => 'none', :fill => (complexity == :minimal ? 'white': 'url(#StarGradient)') )
|
98
|
-
svg.path(:d => "M#{coord.first + scaled(4.5)} #{coord.last - scaled(20)}" +
|
99
|
-
"l-#{scaled(1.25)} #{scaled(5)} l#{scaled(3.75)} -#{scaled(2.5)} l-#{scaled(5)} 0 l#{scaled(3.75)} #{scaled(2.5)} l-#{scaled(1.25)} -#{scaled(5)}", :stroke => 'none', :fill => (complexity == :minimal ? 'white': 'url(#StarGradient)') )
|
100
|
-
svg.path(:d => "M#{coord.first} #{coord.last - scaled(40)}" +
|
101
|
-
"l-#{scaled(1.25)} #{scaled(5)} l#{scaled(3.75)} -#{scaled(2.5)} l-#{scaled(5)} 0 l#{scaled(3.75)} #{scaled(2.5)} l-#{scaled(1.25)} -#{scaled(5)}", :stroke => 'none', :fill => (complexity == :minimal ? 'white': 'url(#StarGradient)') )
|
106
|
+
svg.path(:d => "M#{coord.first} #{coord.last - scaled(60)} " +
|
107
|
+
"L#{coord.first + scaled(10)} #{coord.last - scaled(14)} " +
|
108
|
+
"C#{coord.first + scaled(10)},#{coord.last - scaled(9)} #{coord.first - scaled(10)},#{coord.last - scaled(9)} #{coord.first - scaled(10)},#{coord.last - scaled(14)}" +
|
109
|
+
"L#{coord.first} #{coord.last - scaled(60)}",
|
110
|
+
:stroke => 'black', 'stroke-width' => scaled(1.4), :fill => (complexity == :minimal ? 'purple' : 'url(#HeroGradient)'))
|
102
111
|
|
103
|
-
|
112
|
+
svg.path(:d => "M#{coord.first - scaled(4)} #{coord.last - scaled(23)}" +
|
113
|
+
"l-#{scaled(2.5)} #{scaled(10)} l#{scaled(7.5)} -#{scaled(5)} l-#{scaled(10)} 0 l#{scaled(7.5)} #{scaled(5)} l-#{scaled(2.5)} -#{scaled(10)}", :stroke => 'none', :fill => (complexity == :minimal ? 'white': 'url(#StarGradient)') )
|
114
|
+
svg.path(:d => "M#{coord.first + scaled(2)} #{coord.last - scaled(30)}" +
|
115
|
+
"l-#{scaled(2.5)} #{scaled(10)} l#{scaled(7.5)} -#{scaled(5)} l-#{scaled(10)} 0 l#{scaled(7.5)} #{scaled(5)} l-#{scaled(2.5)} -#{scaled(10)}", :stroke => 'none', :fill => (complexity == :minimal ? 'white': 'url(#StarGradient)') )
|
116
|
+
svg.path(:d => "M#{coord.first - scaled(2)} #{coord.last - scaled(33)}" +
|
117
|
+
"l-#{scaled(1.25)} #{scaled(5)} l#{scaled(3.75)} -#{scaled(2.5)} l-#{scaled(5)} 0 l#{scaled(3.75)} #{scaled(2.5)} l-#{scaled(1.25)} -#{scaled(5)}", :stroke => 'none', :fill => 'white' )
|
118
|
+
svg.path(:d => "M#{coord.first - scaled(2.2)} #{coord.last - scaled(32.7)}" +
|
119
|
+
"l-#{scaled(1.25)} #{scaled(5)} l#{scaled(3.75)} -#{scaled(2.5)} l-#{scaled(5)} 0 l#{scaled(3.75)} #{scaled(2.5)} l-#{scaled(1.25)} -#{scaled(5)}", :stroke => 'none', :fill => (complexity == :minimal ? 'white': 'url(#StarGradient)') )
|
120
|
+
svg.path(:d => "M#{coord.first + scaled(4.5)} #{coord.last - scaled(20)}" +
|
121
|
+
"l-#{scaled(1.25)} #{scaled(5)} l#{scaled(3.75)} -#{scaled(2.5)} l-#{scaled(5)} 0 l#{scaled(3.75)} #{scaled(2.5)} l-#{scaled(1.25)} -#{scaled(5)}", :stroke => 'none', :fill => (complexity == :minimal ? 'white': 'url(#StarGradient)') )
|
122
|
+
svg.path(:d => "M#{coord.first} #{coord.last - scaled(40)}" +
|
123
|
+
"l-#{scaled(1.25)} #{scaled(5)} l#{scaled(3.75)} -#{scaled(2.5)} l-#{scaled(5)} 0 l#{scaled(3.75)} #{scaled(2.5)} l-#{scaled(1.25)} -#{scaled(5)}", :stroke => 'none', :fill => (complexity == :minimal ? 'white': 'url(#StarGradient)') )
|
104
124
|
|
105
125
|
end
|
106
|
-
end
|
107
126
|
|
108
|
-
def scaled(pt)
|
109
|
-
relative(pt) / 2
|
110
127
|
end
|
111
128
|
end
|
129
|
+
|
130
|
+
# Legacy (4 days old). Removed scaled from layout engine,
|
131
|
+
# changed to #relative, with different math involved.
|
132
|
+
# Translate here so I don't have to entirely redo this graph.
|
133
|
+
def scaled(pt)
|
134
|
+
relative(pt) / 2
|
135
|
+
end
|
112
136
|
end
|
113
137
|
end
|
data/lib/scruffy/layers/area.rb
CHANGED
@@ -1,35 +1,43 @@
|
|
1
|
-
module Scruffy
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
module Scruffy::Layers
|
2
|
+
# ==Scruffy::Layers::Area
|
3
|
+
#
|
4
|
+
# Author:: Brasten Sager
|
5
|
+
# Date:: August 6th, 2006
|
6
|
+
#
|
7
|
+
# Standard area graph.
|
8
|
+
class Area < Base
|
9
|
+
|
10
|
+
# Render area graph.
|
11
|
+
def draw(svg, coords, options={})
|
12
|
+
# svg.polygon wants a long string of coords.
|
13
|
+
points_value = "0,#{height} #{stringify_coords(coords).join(' ')} #{width},#{height}"
|
6
14
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
15
|
+
# Experimental, for later user.
|
16
|
+
# This was supposed to add some fun filters, 3d effects and whatnot.
|
17
|
+
# Neither ImageMagick nor Mozilla SVG render this well (at all). Maybe a future thing.
|
18
|
+
#
|
19
|
+
# svg.defs {
|
20
|
+
# svg.filter(:id => 'MyFilter', :filterUnits => 'userSpaceOnUse', :x => 0, :y => 0, :width => 200, :height => '120') {
|
21
|
+
# svg.feGaussianBlur(:in => 'SourceAlpha', :stdDeviation => 4, :result => 'blur')
|
22
|
+
# svg.feOffset(:in => 'blur', :dx => 4, :dy => 4, :result => 'offsetBlur')
|
23
|
+
# svg.feSpecularLighting( :in => 'blur', :surfaceScale => 5, :specularConstant => '.75',
|
24
|
+
# :specularExponent => 20, 'lighting-color' => '#bbbbbb',
|
25
|
+
# :result => 'specOut') {
|
26
|
+
# svg.fePointLight(:x => '-5000', :y => '-10000', :z => '20000')
|
27
|
+
# }
|
28
|
+
#
|
29
|
+
# svg.feComposite(:in => 'specOut', :in2 => 'SourceAlpha', :operator => 'in', :result => 'specOut')
|
30
|
+
# svg.feComposite(:in => 'sourceGraphic', :in2 => 'specOut', :operator => 'arithmetic',
|
31
|
+
# :k1 => 0, :k2 => 1, :k3 => 1, :k4 => 0, :result => 'litPaint')
|
32
|
+
#
|
33
|
+
# svg.feMerge {
|
34
|
+
# svg.feMergeNode(:in => 'offsetBlur')
|
35
|
+
# svg.feMergeNode(:in => 'litPaint')
|
36
|
+
# }
|
37
|
+
# }
|
38
|
+
# }
|
30
39
|
|
31
|
-
|
32
|
-
end
|
40
|
+
svg.polygon(:points => points_value, :fill => color.to_s, :stroke => color.to_s, 'style' => "opacity: #{opacity}")
|
33
41
|
end
|
34
42
|
end
|
35
43
|
end
|
@@ -1,45 +1,62 @@
|
|
1
|
-
module Scruffy
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
1
|
+
module Scruffy::Layers
|
2
|
+
# ==Scruffy::Layers::Average
|
3
|
+
#
|
4
|
+
# Author:: Brasten Sager
|
5
|
+
# Date:: August 7th, 2006
|
6
|
+
#
|
7
|
+
# An 'average' graph. This graph iterates through all the layers and averages
|
8
|
+
# all the data at each point, then draws a thick, translucent, shadowy line graph
|
9
|
+
# indicating the average values.
|
10
|
+
#
|
11
|
+
# This only looks decent in SVG mode. ImageMagick doesn't retain the transparency
|
12
|
+
# for some reason, creating a massive black line. Any help resolving this would
|
13
|
+
# be useful.
|
14
|
+
class Average < Base
|
15
|
+
|
16
|
+
# Returns new Average graph.
|
17
|
+
def initialize(options = {})
|
18
|
+
# Set self's relevant_data to false. Otherwise we get stuck in a
|
19
|
+
# recursive loop.
|
20
|
+
super(options.merge({:relevant_data => false}))
|
21
|
+
end
|
22
|
+
|
23
|
+
# Render average graph.
|
24
|
+
def draw(svg, coords, options = {})
|
25
|
+
svg.polyline( :points => coords.join(' '), :fill => 'none', :stroke => 'black',
|
26
|
+
'stroke-width' => relative(5), 'opacity' => '0.4')
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
# Override default generate_coordinates method to iterate through the layers and
|
31
|
+
# generate coordinates based on the average data points.
|
32
|
+
def generate_coordinates(options = {})
|
33
|
+
key_layer = points.find { |layer| layer.relevant_data? }
|
7
34
|
|
8
|
-
|
9
|
-
svg.polyline( :points => coords.join(' '), :fill => 'none', :stroke => 'black',
|
10
|
-
'stroke-width' => relative(5), 'opacity' => '0.4')
|
11
|
-
end
|
35
|
+
options[:point_distance] = width / (key_layer.points.size - 1).to_f
|
12
36
|
|
13
|
-
|
14
|
-
def generate_coordinates(options = {})
|
15
|
-
key_layer = points.find { |layer| layer.relevant_data? }
|
16
|
-
|
17
|
-
options[:point_distance] = width / (key_layer.points.size - 1).to_f
|
18
|
-
|
19
|
-
coords = []
|
20
|
-
|
21
|
-
key_layer.points.each_with_index do |layer, idx|
|
22
|
-
sum, objects = points.inject([0, 0]) do |arr, elem|
|
23
|
-
if elem.relevant_data?
|
24
|
-
arr[0] += elem.points[idx]
|
25
|
-
arr[1] += 1
|
26
|
-
end
|
27
|
-
arr
|
28
|
-
end
|
37
|
+
coords = []
|
29
38
|
|
30
|
-
|
39
|
+
key_layer.points.each_with_index do |layer, idx|
|
40
|
+
sum, objects = points.inject([0, 0]) do |arr, elem|
|
41
|
+
if elem.relevant_data?
|
42
|
+
arr[0] += elem.points[idx]
|
43
|
+
arr[1] += 1
|
44
|
+
end
|
45
|
+
arr
|
46
|
+
end
|
31
47
|
|
32
|
-
|
48
|
+
average = sum / objects.to_f
|
33
49
|
|
34
|
-
|
35
|
-
y_coord = (height - (height * relative_percent))
|
50
|
+
x_coord = options[:point_distance] * idx
|
36
51
|
|
37
|
-
|
38
|
-
|
52
|
+
relative_percent = ((average == min_value) ? 0 : ((average - min_value) / (max_value - min_value).to_f))
|
53
|
+
y_coord = (height - (height * relative_percent))
|
39
54
|
|
40
|
-
|
55
|
+
coords << [x_coord, y_coord].join(',')
|
41
56
|
end
|
42
|
-
|
43
|
-
|
57
|
+
|
58
|
+
return coords
|
59
|
+
end
|
60
|
+
|
44
61
|
end
|
45
62
|
end
|