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.
@@ -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