ramhoj-scruffy 0.2.6
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/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
|