scruffy 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- module Helpers
3
-
4
- # Helper methods for objects that act as canvases/viewports/etc.
5
- #
6
- # Primarily used for calculating relative positions.
7
- module Canvas
8
-
9
- protected
10
- def bounds_for(canvas_size, position, size)
11
- return nil if (position.nil? || size.nil?)
12
- bounds = {}
13
- bounds[:x] = canvas_size.first * (position.first / 100.to_f)
14
- bounds[:y] = canvas_size.last * (position.last / 100.to_f)
15
- bounds[:width] = canvas_size.first * (size.first / 100.to_f)
16
- bounds[:height] = canvas_size.last * (size.last / 100.to_f)
17
- bounds
18
- end
19
-
20
- end
21
- end
22
- end
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
- # Custom Layer Accessors
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)
@@ -1,4 +1,18 @@
1
- # Layer files
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
- module Layers
3
- class AllSmiles < Base
4
- attr_accessor :standalone
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
- def initialize(options = {})
7
- super
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
- def draw(svg, coords, options={})
12
-
13
- hero_smiley = nil
14
- coords.each { |c| hero_smiley = c.last if (hero_smiley.nil? || c.last < hero_smiley) }
15
-
16
- svg.defs {
17
- svg.radialGradient(:id => 'SmileyGradient', :cx => '50%',
18
- :cy => '50%', :r => '50%', :fx => '30%', :fy => '30%') {
19
-
20
- svg.stop(:offset => '0%', 'stop-color' => '#FFF')
21
- svg.stop(:offset => '20%', 'stop-color' => '#FFC')
22
- svg.stop(:offset => '45%', 'stop-color' => '#FF3')
23
- svg.stop(:offset => '60%', 'stop-color' => '#FF0')
24
- svg.stop(:offset => '90%', 'stop-color' => '#990')
25
- svg.stop(:offset => '100%', 'stop-color' => '#220')
26
- }
27
- svg.radialGradient(:id => 'HeroGradient', :cx => '50%',
28
- :cy => '50%', :r => '50%', :fx => '30%', :fy => '30%') {
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
- unless standalone
48
- svg.polyline( :points => stringify_coords(coords).join(' '), :fill => 'none',
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
- coords.each do |coord|
53
- if standalone
54
- svg.line( :x1 => coord.first, :y1 => coord.last, :x2 => coord.first, :y2 => height, :fill => 'none',
55
- :stroke => '#660', 'stroke-width' => scaled(10), 'stroke-dasharray' => "#{scaled(10)}, #{scaled(10)}" )
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
- percent = 1.0 - (coord.last.to_f / height.to_f)
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
- corners = scaled(8 - (5 * percent))
74
- anchor = scaled((20 * percent) - 5)
75
- svg.path( :d => "M#{coord.first - scaled(9)} #{coord.last + corners} Q#{coord.first} #{coord.last + anchor} #{coord.first + scaled(9)} #{coord.last + corners}",
76
- :stroke => 'black', 'stroke-width' => scaled(1.4), :fill => 'none' )
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
- end
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
@@ -1,35 +1,43 @@
1
- module Scruffy
2
- module Layers
3
- class Area < Base
4
- def draw(svg, coords, options={})
5
- points_value = "0,#{height} #{stringify_coords(coords).join(' ')} #{width},#{height}"
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
- # Experimental, for later user.
8
- # Neither ImageMagick nor Mozilla SVG render this well (at all). Maybe a future thing.
9
- #
10
- # svg.defs {
11
- # svg.filter(:id => 'MyFilter', :filterUnits => 'userSpaceOnUse', :x => 0, :y => 0, :width => 200, :height => '120') {
12
- # svg.feGaussianBlur(:in => 'SourceAlpha', :stdDeviation => 4, :result => 'blur')
13
- # svg.feOffset(:in => 'blur', :dx => 4, :dy => 4, :result => 'offsetBlur')
14
- # svg.feSpecularLighting( :in => 'blur', :surfaceScale => 5, :specularConstant => '.75',
15
- # :specularExponent => 20, 'lighting-color' => '#bbbbbb',
16
- # :result => 'specOut') {
17
- # svg.fePointLight(:x => '-5000', :y => '-10000', :z => '20000')
18
- # }
19
- #
20
- # svg.feComposite(:in => 'specOut', :in2 => 'SourceAlpha', :operator => 'in', :result => 'specOut')
21
- # svg.feComposite(:in => 'sourceGraphic', :in2 => 'specOut', :operator => 'arithmetic',
22
- # :k1 => 0, :k2 => 1, :k3 => 1, :k4 => 0, :result => 'litPaint')
23
- #
24
- # svg.feMerge {
25
- # svg.feMergeNode(:in => 'offsetBlur')
26
- # svg.feMergeNode(:in => 'litPaint')
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
- svg.polygon(:points => points_value, :fill => color.to_s, :stroke => color.to_s, 'style' => "opacity: #{opacity}")
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
- module Layers
3
- class Average < Base
4
- def initialize(options = {})
5
- super(options.merge({:relevant_data => false}))
6
- end
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
- def draw(svg, coords, options = {})
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
- protected
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
- average = sum / objects.to_f
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
- x_coord = options[:point_distance] * idx
48
+ average = sum / objects.to_f
33
49
 
34
- relative_percent = ((average == min_value) ? 0 : ((average - min_value) / (max_value - min_value).to_f))
35
- y_coord = (height - (height * relative_percent))
50
+ x_coord = options[:point_distance] * idx
36
51
 
37
- coords << [x_coord, y_coord].join(',')
38
- end
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
- return coords
55
+ coords << [x_coord, y_coord].join(',')
41
56
  end
42
-
43
- end
57
+
58
+ return coords
59
+ end
60
+
44
61
  end
45
62
  end