ramhoj-scruffy 0.2.6
Sign up to get free protection for your applications and to get access to all the features.
- data/README.txt +66 -0
- data/lib/scruffy.rb +25 -0
- data/lib/scruffy/components.rb +21 -0
- data/lib/scruffy/components/background.rb +24 -0
- data/lib/scruffy/components/base.rb +57 -0
- data/lib/scruffy/components/data_markers.rb +26 -0
- data/lib/scruffy/components/graphs.rb +48 -0
- data/lib/scruffy/components/grid.rb +19 -0
- data/lib/scruffy/components/label.rb +17 -0
- data/lib/scruffy/components/legend.rb +105 -0
- data/lib/scruffy/components/style_info.rb +22 -0
- data/lib/scruffy/components/title.rb +19 -0
- data/lib/scruffy/components/value_markers.rb +32 -0
- data/lib/scruffy/components/viewport.rb +37 -0
- data/lib/scruffy/formatters.rb +213 -0
- data/lib/scruffy/graph.rb +190 -0
- data/lib/scruffy/graph_state.rb +24 -0
- data/lib/scruffy/helpers.rb +12 -0
- data/lib/scruffy/helpers/canvas.rb +41 -0
- data/lib/scruffy/helpers/layer_container.rb +95 -0
- data/lib/scruffy/helpers/meta.rb +5 -0
- data/lib/scruffy/helpers/point_container.rb +70 -0
- data/lib/scruffy/layers.rb +24 -0
- data/lib/scruffy/layers/all_smiles.rb +137 -0
- data/lib/scruffy/layers/area.rb +46 -0
- data/lib/scruffy/layers/average.rb +67 -0
- data/lib/scruffy/layers/bar.rb +52 -0
- data/lib/scruffy/layers/base.rb +191 -0
- data/lib/scruffy/layers/line.rb +46 -0
- data/lib/scruffy/layers/pie.rb +123 -0
- data/lib/scruffy/layers/pie_slice.rb +119 -0
- data/lib/scruffy/layers/scatter.rb +21 -0
- data/lib/scruffy/layers/sparkline_bar.rb +39 -0
- data/lib/scruffy/layers/stacked.rb +87 -0
- data/lib/scruffy/rasterizers.rb +14 -0
- data/lib/scruffy/rasterizers/batik_rasterizer.rb +39 -0
- data/lib/scruffy/rasterizers/rmagick_rasterizer.rb +27 -0
- data/lib/scruffy/renderers.rb +22 -0
- data/lib/scruffy/renderers/base.rb +93 -0
- data/lib/scruffy/renderers/cubed.rb +44 -0
- data/lib/scruffy/renderers/cubed3d.rb +53 -0
- data/lib/scruffy/renderers/empty.rb +22 -0
- data/lib/scruffy/renderers/pie.rb +20 -0
- data/lib/scruffy/renderers/reversed.rb +17 -0
- data/lib/scruffy/renderers/sparkline.rb +10 -0
- data/lib/scruffy/renderers/split.rb +48 -0
- data/lib/scruffy/renderers/standard.rb +36 -0
- data/lib/scruffy/themes.rb +156 -0
- data/lib/scruffy/version.rb +3 -0
- data/spec/output/array.svg +47 -0
- data/spec/output/hash.svg +47 -0
- data/spec/scruffy/graph_spec.rb +175 -0
- data/spec/scruffy/layers/base_spec.rb +30 -0
- data/spec/spec_helper.rb +8 -0
- metadata +155 -0
@@ -0,0 +1,24 @@
|
|
1
|
+
# ===GraphState
|
2
|
+
#
|
3
|
+
# Author:: Brasten Sager
|
4
|
+
# Date:: September 27th, 2007
|
5
|
+
#
|
6
|
+
# State object for holding all of the graph's
|
7
|
+
# settings. Attempting to clean up the
|
8
|
+
# graph interface a bit.
|
9
|
+
|
10
|
+
module Scruffy
|
11
|
+
class GraphState
|
12
|
+
|
13
|
+
attr_accessor :title
|
14
|
+
attr_accessor :theme
|
15
|
+
attr_accessor :default_type
|
16
|
+
attr_accessor :point_markers
|
17
|
+
attr_accessor :value_formatter
|
18
|
+
attr_accessor :rasterizer
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,12 @@
|
|
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
|
+
|
9
|
+
require 'scruffy/helpers/canvas'
|
10
|
+
require 'scruffy/helpers/layer_container'
|
11
|
+
require 'scruffy/helpers/point_container'
|
12
|
+
#require 'scruffy/helpers/meta'
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Scruffy::Helpers
|
2
|
+
|
3
|
+
# ==Scruffy::Helpers::Canvas
|
4
|
+
#
|
5
|
+
# Author:: Brasten Sager
|
6
|
+
# Date:: August 16th, 2006
|
7
|
+
#
|
8
|
+
# Provides common methods for canvas objects. Primarily used for providing
|
9
|
+
# spacial-type calculations where necessary.
|
10
|
+
module Canvas
|
11
|
+
attr_accessor :components
|
12
|
+
|
13
|
+
def reset_settings!
|
14
|
+
self.options = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def component(id, components=self.components)
|
18
|
+
components.find {|elem| elem.id == id}
|
19
|
+
end
|
20
|
+
|
21
|
+
def remove(id, components=self.components)
|
22
|
+
components.delete(component(id))
|
23
|
+
end
|
24
|
+
|
25
|
+
protected
|
26
|
+
# Converts percentage values into actual pixel values based on the known
|
27
|
+
# render size.
|
28
|
+
#
|
29
|
+
# Returns a hash consisting of :x, :y, :width, and :height elements.
|
30
|
+
def bounds_for(canvas_size, position, size)
|
31
|
+
return nil if (position.nil? || size.nil?)
|
32
|
+
bounds = {}
|
33
|
+
bounds[:x] = canvas_size.first * (position.first / 100.to_f)
|
34
|
+
bounds[:y] = canvas_size.last * (position.last / 100.to_f)
|
35
|
+
bounds[:width] = canvas_size.first * (size.first / 100.to_f)
|
36
|
+
bounds[:height] = canvas_size.last * (size.last / 100.to_f)
|
37
|
+
bounds
|
38
|
+
end
|
39
|
+
end # canvas
|
40
|
+
|
41
|
+
end # scruffy::helpers
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Scruffy::Helpers
|
2
|
+
|
3
|
+
# ==Scruffy::Helpers::LayerContainer
|
4
|
+
#
|
5
|
+
# Author:: Brasten Sager
|
6
|
+
# Date:: August 16th, 2006
|
7
|
+
#
|
8
|
+
# Adds some common functionality to any object which needs to act as a
|
9
|
+
# container for graph layers. The best example of this is the Scruffy::Graph
|
10
|
+
# object itself, but this module is also used by Scruffy::Layer::Stacked.
|
11
|
+
module LayerContainer
|
12
|
+
|
13
|
+
# Adds a Layer to the Graph/Container. Accepts either a list of
|
14
|
+
# arguments used to build a new layer, or a Scruffy::Layers::Base-derived
|
15
|
+
# object. When passing a list of arguments, all arguments are optional,
|
16
|
+
# but the arguments specified must be provided in a particular order:
|
17
|
+
# type (Symbol), title (String), points (Array), options (Hash).
|
18
|
+
#
|
19
|
+
# Both #add and #<< can be used.
|
20
|
+
#
|
21
|
+
# graph.add(:line, [100, 200, 150]) # Create and add an untitled line graph
|
22
|
+
#
|
23
|
+
# graph << (:line, "John's Sales", [150, 100]) # Create and add a titled line graph
|
24
|
+
#
|
25
|
+
# graph << Scruffy::Layers::Bar.new({...}) # Adds Bar layer to graph
|
26
|
+
#
|
27
|
+
def <<(*args, &block)
|
28
|
+
if args[0].kind_of?(Scruffy::Layers::Base)
|
29
|
+
layers << args[0]
|
30
|
+
else
|
31
|
+
type = args.first.is_a?(Symbol) ? args.shift : @default_type
|
32
|
+
title = args.shift if args.first.is_a?(String)
|
33
|
+
|
34
|
+
# Layer handles PointContainer mixin, don't do it here
|
35
|
+
points = [Array, Hash].include?(args.first.class) ? args.shift : []
|
36
|
+
options = args.first.is_a?(Hash) ? args.shift : {}
|
37
|
+
|
38
|
+
title ||= ''
|
39
|
+
|
40
|
+
raise ArgumentError,
|
41
|
+
'You must specify a graph type (:area, :bar, :line, etc) if you do not have a default type specified.' if type.nil?
|
42
|
+
|
43
|
+
class_name = "Scruffy::Layers::#{to_camelcase(type.to_s)}"
|
44
|
+
layer_class = Kernel::module_eval(class_name)
|
45
|
+
options = {:points => points, :title => title}.merge options
|
46
|
+
layer = layer_class.new(options, &block)
|
47
|
+
layers << layer
|
48
|
+
end
|
49
|
+
layer
|
50
|
+
end
|
51
|
+
|
52
|
+
alias :add :<<
|
53
|
+
|
54
|
+
|
55
|
+
# Layer Writer
|
56
|
+
def layers=(val)
|
57
|
+
@layers = val
|
58
|
+
end
|
59
|
+
|
60
|
+
# Layer Reader
|
61
|
+
def layers
|
62
|
+
@layers ||= []
|
63
|
+
end
|
64
|
+
|
65
|
+
# Returns the highest value in any of this container's layers.
|
66
|
+
#
|
67
|
+
# If padding is set to :padded, a 15% padding is added to the highest value.
|
68
|
+
def top_value(padding=nil) # :nodoc:
|
69
|
+
topval = layers.inject(0) { |max, layer| (max = ((max < layer.top_value) ? layer.top_value : max)) unless layer.top_value.nil?; max }
|
70
|
+
padding == :padded ? (topval - ((topval - bottom_value) * 0.15)) : topval
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns the lowest value in any of this container's layers.
|
74
|
+
#
|
75
|
+
# If padding is set to :padded, a 15% padding is added below the lowest value.
|
76
|
+
# If the lowest value is greater than zero, then the padding will not cross the zero line, preventing
|
77
|
+
# negative values from being introduced into the graph purely due to padding.
|
78
|
+
def bottom_value(padding=nil) # :nodoc:
|
79
|
+
botval = layers.inject(top_value) { |min, layer| (min = ((min > layer.bottom_value) ? layer.bottom_value : min)) unless layer.bottom_value.nil?; min }
|
80
|
+
above_zero = (botval > 0)
|
81
|
+
botval = (botval - ((top_value - botval) * 0.15))
|
82
|
+
|
83
|
+
# Don't introduce negative values solely due to padding.
|
84
|
+
# A user-provided value must be negative before padding will extend into negative values.
|
85
|
+
(above_zero && botval < 0) ? 0 : botval
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
protected
|
90
|
+
def to_camelcase(type) # :nodoc:
|
91
|
+
type.split('_').map { |e| e.capitalize }.join('')
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Scruffy::Helpers
|
2
|
+
|
3
|
+
# ==Scruffy::Helpers::PointContainer
|
4
|
+
#
|
5
|
+
# Author:: Mat Schaffer
|
6
|
+
# Date:: March 22nd, 2007
|
7
|
+
#
|
8
|
+
# Allows all standard point operations to be called on both Array and Hash
|
9
|
+
module PointContainer
|
10
|
+
def self.extended point_set
|
11
|
+
point_set.extend(const_get(point_set.class.to_s))
|
12
|
+
end
|
13
|
+
|
14
|
+
def sortable_values
|
15
|
+
values.find_all { |v| v.respond_to? :<=> }
|
16
|
+
end
|
17
|
+
|
18
|
+
def summable_values
|
19
|
+
values.find_all { |v| v.respond_to? :+ }
|
20
|
+
end
|
21
|
+
|
22
|
+
def maximum_value
|
23
|
+
sortable_values.sort.last
|
24
|
+
end
|
25
|
+
|
26
|
+
def minimum_value
|
27
|
+
sortable_values.sort.first
|
28
|
+
end
|
29
|
+
|
30
|
+
def sum
|
31
|
+
summable_values.inject(0) { |sum, i| sum += i }
|
32
|
+
end
|
33
|
+
|
34
|
+
def inject_with_index memo
|
35
|
+
index = 0
|
36
|
+
inject(memo) do |memo, item|
|
37
|
+
ret = yield memo, item, index
|
38
|
+
index = index.succ
|
39
|
+
ret
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
module Array
|
44
|
+
def values
|
45
|
+
self
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
module Hash
|
50
|
+
def minimum_key
|
51
|
+
self.keys.sort.first
|
52
|
+
end
|
53
|
+
|
54
|
+
def maximum_key
|
55
|
+
self.keys.sort.last
|
56
|
+
end
|
57
|
+
|
58
|
+
def inject memo
|
59
|
+
(minimum_key..maximum_key).each do |i|
|
60
|
+
memo = yield memo, self[i]
|
61
|
+
end
|
62
|
+
memo
|
63
|
+
end
|
64
|
+
|
65
|
+
def size
|
66
|
+
maximum_key - minimum_key + 1
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,24 @@
|
|
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
|
+
|
16
|
+
require 'scruffy/layers/base'
|
17
|
+
require 'scruffy/layers/area'
|
18
|
+
require 'scruffy/layers/all_smiles'
|
19
|
+
require 'scruffy/layers/bar'
|
20
|
+
require 'scruffy/layers/line'
|
21
|
+
require 'scruffy/layers/average'
|
22
|
+
require 'scruffy/layers/stacked'
|
23
|
+
require 'scruffy/layers/pie'
|
24
|
+
require 'scruffy/layers/pie_slice'
|
@@ -0,0 +1,137 @@
|
|
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={})
|
31
|
+
|
32
|
+
hero_smiley = nil
|
33
|
+
coords.each { |c| hero_smiley = c.last if (hero_smiley.nil? || c.last < hero_smiley) }
|
34
|
+
|
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')
|
54
|
+
}
|
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',
|
75
|
+
:stroke => '#660', 'stroke-width' => scaled(10), 'stroke-dasharray' => "#{scaled(10)}, #{scaled(10)}" )
|
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
|
+
|
90
|
+
|
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)
|
95
|
+
|
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) )
|
105
|
+
|
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)'))
|
111
|
+
|
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)') )
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
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
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,46 @@
|
|
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}"
|
14
|
+
|
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
|
+
# }
|
39
|
+
svg.g(:transform => "translate(0, -#{relative(2)})") {
|
40
|
+
svg.polygon(:points => points_value, :style => "fill: black; stroke: black; fill-opacity: 0.06; stroke-opacity: 0.06;")
|
41
|
+
}
|
42
|
+
|
43
|
+
svg.polygon(:points => points_value, :fill => color.to_s, :stroke => color.to_s, 'style' => "opacity: #{opacity}")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|