scruffy 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGES +8 -0
- data/Rakefile +9 -2
- data/lib/scruffy.rb +13 -0
- data/lib/scruffy/components.rb +10 -0
- data/lib/scruffy/components/viewport.rb +29 -17
- data/lib/scruffy/formatters.rb +156 -91
- data/lib/scruffy/graph.rb +22 -24
- data/lib/scruffy/helpers.rb +8 -0
- data/lib/scruffy/helpers/canvas.rb +25 -22
- data/lib/scruffy/helpers/layer_container.rb +20 -2
- data/lib/scruffy/layers.rb +15 -1
- data/lib/scruffy/layers/all_smiles.rb +121 -97
- data/lib/scruffy/layers/area.rb +38 -30
- data/lib/scruffy/layers/average.rb +52 -35
- data/lib/scruffy/layers/bar.rb +35 -22
- data/lib/scruffy/layers/base.rb +138 -126
- data/lib/scruffy/layers/line.rb +15 -9
- data/lib/scruffy/layers/sparkline_bar.rb +38 -0
- data/lib/scruffy/layers/stacked.rb +74 -59
- data/lib/scruffy/rasterizers.rb +13 -1
- data/lib/scruffy/rasterizers/batik_rasterizer.rb +35 -21
- data/lib/scruffy/rasterizers/rmagick_rasterizer.rb +19 -22
- data/lib/scruffy/renderers.rb +17 -1
- data/lib/scruffy/renderers/base.rb +39 -31
- data/lib/scruffy/renderers/cubed.rb +39 -31
- data/lib/scruffy/renderers/cubed3d.rb +53 -0
- data/lib/scruffy/renderers/empty.rb +23 -0
- data/lib/scruffy/renderers/sparkline.rb +11 -0
- data/lib/scruffy/themes.rb +81 -61
- data/lib/scruffy/version.rb +1 -1
- metadata +16 -4
data/CHANGES
CHANGED
data/Rakefile
CHANGED
@@ -39,11 +39,14 @@ Rake::RDocTask.new { |rdoc|
|
|
39
39
|
rdoc.title = "Scruffy - Graphing Library for Ruby"
|
40
40
|
# rdoc.options << '--line-numbers --inline-source --main README --accessor adv_attr_accessor=M'
|
41
41
|
# rdoc.template = "#{ENV['template']}.rb" if ENV['template']
|
42
|
-
rdoc.rdoc_files.include('README', 'CHANGES')
|
42
|
+
rdoc.rdoc_files.include('README', 'CHANGES', 'MIT-LICENSE')
|
43
43
|
rdoc.rdoc_files.include('lib/scruffy.rb')
|
44
44
|
rdoc.rdoc_files.include('lib/scruffy/*.rb')
|
45
45
|
rdoc.rdoc_files.include('lib/scruffy/layers/*.rb')
|
46
46
|
rdoc.rdoc_files.include('lib/scruffy/renderers/*.rb')
|
47
|
+
rdoc.rdoc_files.include('lib/scruffy/components/*.rb')
|
48
|
+
rdoc.rdoc_files.include('lib/scruffy/helpers/*.rb')
|
49
|
+
rdoc.rdoc_files.include('lib/scruffy/rasterizers/*.rb')
|
47
50
|
}
|
48
51
|
|
49
52
|
spec = Gem::Specification.new do |s|
|
@@ -53,6 +56,7 @@ spec = Gem::Specification.new do |s|
|
|
53
56
|
s.email = "brasten@nagilum.com"
|
54
57
|
s.homepage = "http://scruffy.rubyforge.org"
|
55
58
|
s.summary = "A powerful, clean graphing library for Ruby."
|
59
|
+
s.add_dependency('builder', '>= 2.0')
|
56
60
|
s.description = <<-EOF
|
57
61
|
Scruffy is a Ruby library for generating powerful graphs. It is based on
|
58
62
|
SVG, allowing for powerful, clean code, as well as a good foundation for
|
@@ -84,7 +88,10 @@ task :publish_packages => [:verify_user, :verify_password, :package] do
|
|
84
88
|
release_files = FileList[
|
85
89
|
"pkg/#{PKG_FILE_NAME}.gem",
|
86
90
|
"pkg/#{PKG_FILE_NAME}.tgz",
|
87
|
-
"pkg/#{PKG_FILE_NAME}.zip"
|
91
|
+
"pkg/#{PKG_FILE_NAME}.zip",
|
92
|
+
"pkg/#{PKG_FILE_NAME}.gem.md5",
|
93
|
+
"pkg/#{PKG_FILE_NAME}.tgz.md5",
|
94
|
+
"pkg/#{PKG_FILE_NAME}.zip.md5"
|
88
95
|
]
|
89
96
|
|
90
97
|
Rake::XForge::Release.new(MetaProject::Project::XForge::RubyForge.new(PKG_NAME)) do |xf|
|
data/lib/scruffy.rb
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
# ===Scruffy Graphing Library for Ruby
|
2
|
+
#
|
3
|
+
# Author:: Brasten Sager
|
4
|
+
# Date:: August 5th, 2006
|
5
|
+
#
|
6
|
+
# For information on generating graphs using Scruffy, see the
|
7
|
+
# documentation in Scruffy::Graph.
|
8
|
+
#
|
9
|
+
# For information on creating your own graph types, see the
|
10
|
+
# documentation in Scruffy::Layers::Base.
|
11
|
+
module Scruffy; end
|
12
|
+
|
13
|
+
|
1
14
|
$:.unshift(File.dirname(__FILE__)) unless
|
2
15
|
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
16
|
|
data/lib/scruffy/components.rb
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
# ===Scruffy Components
|
2
|
+
#
|
3
|
+
# Author:: Brasten Sager
|
4
|
+
# Date:: August 16th, 2006
|
5
|
+
#
|
6
|
+
# Components make up the visual elements of a Scruffy graph.
|
7
|
+
#
|
8
|
+
# For examples, see Scruffy::Components::Base.
|
9
|
+
module Scruffy::Components; end
|
10
|
+
|
1
11
|
require 'scruffy/components/base'
|
2
12
|
require 'scruffy/components/title'
|
3
13
|
require 'scruffy/components/background'
|
@@ -1,21 +1,21 @@
|
|
1
|
-
module Scruffy
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
module Scruffy::Components
|
2
|
+
# Component used to limit other visual components to a certain area on the graph.
|
3
|
+
class Viewport < Base
|
4
|
+
include Scruffy::Helpers::Canvas
|
5
|
+
|
6
|
+
attr_accessor :components
|
7
|
+
|
8
|
+
def initialize(*args, &block)
|
9
|
+
super(*args)
|
6
10
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
super(*args)
|
11
|
-
|
12
|
-
self.components = []
|
13
|
-
if block
|
14
|
-
block.call(self.components)
|
15
|
-
end
|
11
|
+
self.components = []
|
12
|
+
if block
|
13
|
+
block.call(self.components)
|
16
14
|
end
|
15
|
+
end
|
17
16
|
|
18
|
-
|
17
|
+
def draw(svg, bounds, options={})
|
18
|
+
svg.g(options_for) {
|
19
19
|
self.components.each do |component|
|
20
20
|
component.render(svg,
|
21
21
|
bounds_for( [bounds[:width], bounds[:height]],
|
@@ -23,8 +23,20 @@ module Scruffy
|
|
23
23
|
component.size ),
|
24
24
|
options)
|
25
25
|
end
|
26
|
-
|
27
|
-
end
|
26
|
+
}
|
28
27
|
end
|
28
|
+
|
29
|
+
private
|
30
|
+
def options_for
|
31
|
+
options = {}
|
32
|
+
%w(skewX skewY).each do |option|
|
33
|
+
if @options[option.to_sym]
|
34
|
+
options[:transform] ||= ''
|
35
|
+
options[:transform] = options[:transform] + "#{option.to_s}(#{@options[option.to_sym]})"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
options
|
40
|
+
end
|
29
41
|
end
|
30
42
|
end
|
data/lib/scruffy/formatters.rb
CHANGED
@@ -1,111 +1,176 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
1
|
+
# ===Scruffy Formatters
|
2
|
+
#
|
3
|
+
# Author:: Brasten Sager
|
4
|
+
# Date:: August 16th, 2006
|
5
|
+
#
|
6
|
+
# Formatters are used to format the values displayed on the y-axis by
|
7
|
+
# setting graph.value_formatter.
|
8
|
+
#
|
9
|
+
# Example:
|
10
|
+
#
|
11
|
+
# graph.value_formatter = Scruffy::Formatters::Currency.new(:precision => 0)
|
12
|
+
#
|
13
|
+
module Scruffy::Formatters
|
14
|
+
|
15
|
+
# == Scruffy::Formatters::Base
|
16
|
+
#
|
17
|
+
# Author:: Brasten Sager
|
18
|
+
# Date:: August 16th, 2006
|
19
|
+
#
|
20
|
+
# Formatters are used to format the values displayed on the y-axis by
|
21
|
+
# setting graph.value_formatter.
|
22
|
+
class Base
|
23
|
+
|
24
|
+
# Called by the value marker component. Routes the format call
|
25
|
+
# to one of a couple possible methods.
|
26
|
+
#
|
27
|
+
# If the formatter defines a #format method, the returned value is used
|
28
|
+
# as the value. If the formatter defines a #format! method, the value passed is
|
29
|
+
# expected to be modified, and is used as the value. (This may not actually work,
|
30
|
+
# in hindsight.)
|
31
|
+
def route_format(target, idx, options = {})
|
32
|
+
args = [target, idx, options]
|
33
|
+
if respond_to?(:format)
|
34
|
+
send :format, *args[0...self.method(:format).arity]
|
35
|
+
elsif respond_to?(:format!)
|
36
|
+
send :format!, *args[0...self.method(:format!).arity]
|
37
|
+
target
|
38
|
+
else
|
39
|
+
raise NameError, "Formatter subclass must container either a format() method or format!() method."
|
14
40
|
end
|
41
|
+
end
|
15
42
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
43
|
+
protected
|
44
|
+
def number_with_precision(number, precision=3) #:nodoc:
|
45
|
+
sprintf("%01.#{precision}f", number)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Default number formatter.
|
50
|
+
# Limits precision, beautifies numbers.
|
51
|
+
class Number < Base
|
52
|
+
attr_accessor :precision, :separator, :delimiter, :precision_limit
|
53
|
+
|
54
|
+
# Returns a new Number formatter.
|
55
|
+
#
|
56
|
+
# Options:
|
57
|
+
# precision:: precision to use for value. Can be set to an integer, :none or :auto.
|
58
|
+
# :auto will use whatever precision is necessary to portray all the numerical
|
59
|
+
# information, up to :precision_limit.
|
60
|
+
#
|
61
|
+
# Example: [100.1, 100.44, 200.323] will result in [100.100, 100.440, 200.323]
|
62
|
+
#
|
63
|
+
# separator:: decimal separator. Defaults to '.'
|
64
|
+
# delimiter:: delimiter character. Defaults to ','
|
65
|
+
# precision_limit:: upper limit for auto precision.
|
66
|
+
def initialize(options = {})
|
67
|
+
@precision = options[:precision] || :none
|
68
|
+
@separator = options[:separator] || '.'
|
69
|
+
@delimiter = options[:delimiter] || ','
|
70
|
+
@precision_limit = options[:precision_limit] || 4
|
20
71
|
end
|
21
72
|
|
22
|
-
#
|
23
|
-
|
24
|
-
|
73
|
+
# Formats the value.
|
74
|
+
def format(target, idx, options)
|
75
|
+
my_precision = @precision
|
25
76
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
77
|
+
if @precision == :auto
|
78
|
+
my_precision = options[:all_values].inject(0) do |highest, current|
|
79
|
+
cur = current.to_f.to_s.split(".").last.size
|
80
|
+
cur > highest ? cur : highest
|
81
|
+
end
|
82
|
+
|
83
|
+
my_precision = @precision_limit if my_precision > @precision_limit
|
84
|
+
elsif @precision == :none
|
85
|
+
my_precision = 0
|
31
86
|
end
|
32
87
|
|
33
|
-
|
34
|
-
|
88
|
+
my_separator = @separator
|
89
|
+
my_separator = "" unless my_precision > 0
|
90
|
+
begin
|
91
|
+
parts = number_with_precision(target, my_precision).split('.')
|
35
92
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
end
|
41
|
-
|
42
|
-
my_precision = @precision_limit if my_precision > @precision_limit
|
43
|
-
elsif @precision == :none
|
44
|
-
my_precision = 0
|
45
|
-
end
|
46
|
-
|
47
|
-
my_separator = @separator
|
48
|
-
my_separator = "" unless my_precision > 0
|
49
|
-
begin
|
50
|
-
parts = number_with_precision(target, my_precision).split('.')
|
51
|
-
|
52
|
-
number = parts[0].to_s.gsub(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{@delimiter}") + my_separator + parts[1].to_s
|
53
|
-
number
|
54
|
-
rescue StandardError => e
|
55
|
-
target
|
56
|
-
end
|
93
|
+
number = parts[0].to_s.gsub(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{@delimiter}") + my_separator + parts[1].to_s
|
94
|
+
number
|
95
|
+
rescue StandardError => e
|
96
|
+
target
|
57
97
|
end
|
58
98
|
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Currency formatter.
|
102
|
+
#
|
103
|
+
# Provides formatting for currencies.
|
104
|
+
class Currency < Base
|
59
105
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
106
|
+
# Returns a new Currency class.
|
107
|
+
#
|
108
|
+
# Options:
|
109
|
+
# precision:: precision of value
|
110
|
+
# unit:: Defaults to '$'
|
111
|
+
# separator:: Defaults to '.'
|
112
|
+
# delimiter:: Defaults to ','
|
113
|
+
# negative_color:: Color of value marker for negative values. Defaults to 'red'
|
114
|
+
# special_negatives:: If set to true, parenthesizes negative numbers. ie: -$150.50 becomes ($150.50).
|
115
|
+
# Defaults to false.
|
116
|
+
def initialize(options = {})
|
117
|
+
@precision = options[:precision] || 2
|
118
|
+
@unit = options[:unit] || '$'
|
119
|
+
@separator = options[:separator] || '.'
|
120
|
+
@delimiter = options[:delimiter] || ','
|
121
|
+
@negative_color = options[:negative_color] || 'red'
|
122
|
+
@special_negatives = options[:special_negatives] || false
|
123
|
+
end
|
124
|
+
|
125
|
+
# Formats value marker.
|
126
|
+
def format(target, idx, options)
|
127
|
+
@separator = "" unless @precision > 0
|
128
|
+
begin
|
129
|
+
parts = number_with_precision(target, @precision).split('.')
|
130
|
+
if @special_negatives && (target.to_f < 0)
|
131
|
+
number = "(" + @unit + parts[0].to_i.abs.to_s.gsub(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{@delimiter}") + @separator + parts[1].to_s + ")"
|
132
|
+
else
|
133
|
+
number = @unit + parts[0].to_s.gsub(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{@delimiter}") + @separator + parts[1].to_s
|
134
|
+
end
|
135
|
+
if (target.to_f < 0) && @negative_color
|
136
|
+
options[:marker_color_override] = @negative_color
|
85
137
|
end
|
138
|
+
number
|
139
|
+
rescue
|
140
|
+
target
|
86
141
|
end
|
87
142
|
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# Percentage formatter.
|
146
|
+
#
|
147
|
+
# Provides formatting for percentages.
|
148
|
+
class Percentage < Base
|
149
|
+
|
150
|
+
# Returns new Percentage formatter.
|
151
|
+
#
|
152
|
+
# Options:
|
153
|
+
# precision:: Defaults to 3.
|
154
|
+
# separator:: Defaults to '.'
|
155
|
+
def initialize(options = {})
|
156
|
+
@precision = options[:precision] || 3
|
157
|
+
@separator = options[:separator] || '.'
|
158
|
+
end
|
88
159
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
parts = number.split('.')
|
99
|
-
if parts.at(1).nil?
|
100
|
-
parts[0] + "%"
|
101
|
-
else
|
102
|
-
parts[0] + @separator + parts[1].to_s + "%"
|
103
|
-
end
|
104
|
-
rescue
|
105
|
-
target
|
160
|
+
# Formats percentages.
|
161
|
+
def format(target)
|
162
|
+
begin
|
163
|
+
number = number_with_precision(target, @precision)
|
164
|
+
parts = number.split('.')
|
165
|
+
if parts.at(1).nil?
|
166
|
+
parts[0] + "%"
|
167
|
+
else
|
168
|
+
parts[0] + @separator + parts[1].to_s + "%"
|
106
169
|
end
|
170
|
+
rescue
|
171
|
+
target
|
107
172
|
end
|
108
173
|
end
|
109
|
-
|
110
174
|
end
|
175
|
+
|
111
176
|
end
|
data/lib/scruffy/graph.rb
CHANGED
@@ -1,13 +1,3 @@
|
|
1
|
-
# ===Scruffy Graphing Library for Ruby
|
2
|
-
#
|
3
|
-
# Author:: Brasten Sager
|
4
|
-
# Date:: August 5th, 2006
|
5
|
-
#
|
6
|
-
# For information on generating graphs using Scruffy, see the
|
7
|
-
# documentation in Scruffy::Graph.
|
8
|
-
#
|
9
|
-
# For information on creating your own graph types, see the
|
10
|
-
# documentation in Scruffy::Layers::Base.
|
11
1
|
module Scruffy
|
12
2
|
|
13
3
|
# ==Scruffy Graphs
|
@@ -21,7 +11,7 @@ module Scruffy
|
|
21
11
|
# Scruffy::Graph is the primary class you will use to generate your graphs. A Graph does not
|
22
12
|
# define a graph type nor does it directly hold any data. Instead, a Graph object can be thought
|
23
13
|
# of as a canvas on which other graphs are draw. (The actual graphs themselves are subclasses of Scruffy::Layers::Base)
|
24
|
-
# Despite the technical distinction, we will refer to Scruffy::Graph objects as 'graphs' and Scruffy::
|
14
|
+
# Despite the technical distinction, we will refer to Scruffy::Graph objects as 'graphs' and Scruffy::Layers as
|
25
15
|
# 'layers' or 'graph types.'
|
26
16
|
#
|
27
17
|
#
|
@@ -34,7 +24,7 @@ module Scruffy
|
|
34
24
|
#
|
35
25
|
# OR
|
36
26
|
#
|
37
|
-
# graph = Scruffy::Graph.new(:title => "Monthly Profits", :theme => Scruffy::Themes::
|
27
|
+
# graph = Scruffy::Graph.new(:title => "Monthly Profits", :theme => Scruffy::Themes::RubyBlog.new)
|
38
28
|
#
|
39
29
|
# Once you have a Graph object, you can set any Graph-level properties (title, theme, etc), or begin adding
|
40
30
|
# graph layers. You can add a graph layer to a graph by using the Graph#add or Graph#<< methods. The two
|
@@ -49,13 +39,21 @@ module Scruffy
|
|
49
39
|
# graph << Scruffy::Layers::Line.new(:title => 'Sara', :points => [120, 50, -80, 20])
|
50
40
|
#
|
51
41
|
# Now that we've created our graph and added a layer to it, we're ready to render! You can render the graph
|
52
|
-
# directly to SVG with the Graph#render method:
|
42
|
+
# directly to SVG or any other image format (supported by RMagick) with the Graph#render method:
|
53
43
|
#
|
54
44
|
# graph.render # Renders a 600x400 SVG graph
|
55
45
|
#
|
56
46
|
# OR
|
57
47
|
#
|
58
|
-
# graph.render(:
|
48
|
+
# graph.render(:width => 1200)
|
49
|
+
#
|
50
|
+
# # For image formats other than SVG:
|
51
|
+
# graph.render(:width => 1200, :as => 'PNG')
|
52
|
+
#
|
53
|
+
# # To render directly to a file:
|
54
|
+
# graph.render(:width => 5000, :to => '<filename>')
|
55
|
+
#
|
56
|
+
# graph.render(:width => 700, :as => 'PNG', :to => '<filename>')
|
59
57
|
#
|
60
58
|
# And that's your basic Scruffy graph! Please check the documentation for the various methods and
|
61
59
|
# classes you'll be using, as there are a bunch of options not demonstrated here.
|
@@ -93,7 +91,7 @@ module Scruffy
|
|
93
91
|
# Options:
|
94
92
|
#
|
95
93
|
# title:: Graph's title
|
96
|
-
# theme:: A theme
|
94
|
+
# theme:: A theme object to use when rendering graph
|
97
95
|
# layers:: An array of Layers for this graph to use
|
98
96
|
# default_type:: A symbol indicating the default type of Layer for this graph
|
99
97
|
# value_formatter:: Sets a formatter used to modify marker values prior to rendering
|
@@ -128,7 +126,7 @@ module Scruffy
|
|
128
126
|
#
|
129
127
|
# For other image formats:
|
130
128
|
# as:: File format to render to ('PNG', 'JPG', etc)
|
131
|
-
# to:: Name of file to save graph to, if desired. If not provided, image is returned as blob.
|
129
|
+
# to:: Name of file to save graph to, if desired. If not provided, image is returned as blob/string.
|
132
130
|
def render(options = {})
|
133
131
|
options[:theme] ||= theme
|
134
132
|
options[:value_formatter] ||= value_formatter
|
@@ -140,16 +138,16 @@ module Scruffy
|
|
140
138
|
options[:max_value] ||= top_value
|
141
139
|
options[:graph] ||= self
|
142
140
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
141
|
+
|
142
|
+
# Removed for now.
|
143
|
+
# Added for making smaller fonts more legible, but may not be needed after all.
|
144
|
+
#
|
145
|
+
# if options[:as] && (options[:size][0] <= 300 || options[:size][1] <= 200)
|
146
|
+
# options[:actual_size] = options[:size]
|
147
|
+
# options[:size] = [800, (800.to_f * (options[:actual_size][1].to_f / options[:actual_size][0].to_f))]
|
148
|
+
# end
|
149
149
|
|
150
150
|
svg = ( options[:renderer].nil? ? self.renderer.render( options ) : options[:renderer].render( options ) )
|
151
|
-
|
152
|
-
options[:size] = options[:actual_size] if options[:actual_size]
|
153
151
|
|
154
152
|
# SVG to file.
|
155
153
|
if options[:to] && options[:as].nil?
|