scruffy 0.2.6 → 0.3.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- data/{History.txt → CHANGES.txt} +15 -12
- data/README.txt +25 -25
- data/lib/scruffy.rb +0 -5
- data/lib/scruffy/components.rb +1 -0
- data/lib/scruffy/components/axes.rb +23 -0
- data/lib/scruffy/components/background.rb +3 -3
- data/lib/scruffy/components/base.rb +3 -0
- data/lib/scruffy/components/data_markers.rb +23 -8
- data/lib/scruffy/components/graphs.rb +4 -0
- data/lib/scruffy/components/grid.rb +45 -4
- data/lib/scruffy/components/legend.rb +63 -21
- data/lib/scruffy/components/title.rb +1 -1
- data/lib/scruffy/components/value_markers.rb +9 -16
- data/lib/scruffy/formatters.rb +41 -3
- data/lib/scruffy/graph.rb +27 -11
- data/lib/scruffy/graph_state.rb +5 -0
- data/lib/scruffy/helpers.rb +1 -0
- data/lib/scruffy/helpers/layer_container.rb +28 -4
- data/lib/scruffy/helpers/marker_helper.rb +25 -0
- data/lib/scruffy/helpers/point_container.rb +46 -17
- data/lib/scruffy/layers.rb +6 -1
- data/lib/scruffy/layers/bar.rb +35 -14
- data/lib/scruffy/layers/base.rb +51 -21
- data/lib/scruffy/layers/box.rb +114 -0
- data/lib/scruffy/layers/line.rb +31 -14
- data/lib/scruffy/layers/multi.rb +74 -0
- data/lib/scruffy/layers/multi_area.rb +119 -0
- data/lib/scruffy/layers/multi_bar.rb +51 -0
- data/lib/scruffy/layers/scatter.rb +13 -5
- data/lib/scruffy/layers/stacked.rb +2 -1
- data/lib/scruffy/rasterizers.rb +1 -0
- data/lib/scruffy/rasterizers/mini_magick_rasterizer.rb +24 -0
- data/lib/scruffy/rasterizers/rmagick_rasterizer.rb +8 -2
- data/lib/scruffy/renderers.rb +2 -0
- data/lib/scruffy/renderers/axis_legend.rb +41 -0
- data/lib/scruffy/renderers/base.rb +6 -4
- data/lib/scruffy/renderers/basic.rb +20 -0
- data/lib/scruffy/renderers/standard.rb +7 -6
- data/lib/scruffy/themes.rb +37 -1
- data/lib/scruffy/version.rb +1 -7
- data/spec/output/array.svg +55 -0
- data/spec/output/hash.svg +55 -0
- data/spec/scruffy/graph_spec.rb +4 -4
- data/spec/scruffy/layers/base_spec.rb +15 -10
- metadata +84 -96
- data/CHANGES +0 -104
- data/License.txt +0 -20
- data/MIT-LICENSE +0 -20
- data/Manifest.txt +0 -104
- data/PostInstall.txt +0 -6
- data/README +0 -9
- data/Rakefile +0 -108
- data/config/hoe.rb +0 -78
- data/config/requirements.rb +0 -15
- data/script/console +0 -10
- data/script/destroy +0 -14
- data/script/generate +0 -14
- data/script/txt2html +0 -82
- data/setup.rb +0 -1585
- data/spec/scruffy/layers/line_spec.rb +0 -10
- data/tasks/deployment.rake +0 -34
- data/tasks/environment.rake +0 -7
- data/tasks/website.rake +0 -17
- data/test/graph_creation_test.rb +0 -101
- data/test/test_helper.rb +0 -2
- data/website/images/blank.gif.html +0 -7
- data/website/images/graphs/all_smiles.png +0 -0
- data/website/images/graphs/bar_test.png +0 -0
- data/website/images/graphs/bar_test.svg +0 -71
- data/website/images/graphs/line_test.png +0 -0
- data/website/images/graphs/line_test.svg +0 -60
- data/website/images/graphs/multi_test.png +0 -0
- data/website/images/graphs/multi_test.svg +0 -296
- data/website/images/graphs/pie_test.png +0 -0
- data/website/images/graphs/pie_test.svg +0 -40
- data/website/images/graphs/split_test.png +0 -0
- data/website/images/graphs/split_test.svg +0 -295
- data/website/images/graphs/stacking_test.png +0 -0
- data/website/images/graphs/stacking_test.svg +0 -146
- data/website/images/header.png +0 -0
- data/website/images/header_gradient.png +0 -0
- data/website/images/overlay.png +0 -0
- data/website/images/scruffy.png +0 -0
- data/website/index.html +0 -225
- data/website/index.txt +0 -204
- data/website/javascripts/application.js +0 -2
- data/website/javascripts/controls.js +0 -815
- data/website/javascripts/dragdrop.js +0 -913
- data/website/javascripts/effects.js +0 -958
- data/website/javascripts/lightbox.js +0 -437
- data/website/javascripts/prototype.js +0 -2006
- data/website/javascripts/rounded_corners_lite.inc.js +0 -285
- data/website/stylesheets/lightbox.css +0 -27
- data/website/stylesheets/screen.css +0 -147
- data/website/stylesheets/scruffy.css +0 -227
- data/website/template.html.erb +0 -47
@@ -7,7 +7,7 @@ module Scruffy
|
|
7
7
|
:class => 'title',
|
8
8
|
:x => (bounds[:width] / 2),
|
9
9
|
:y => bounds[:height],
|
10
|
-
'font-size' => relative(100),
|
10
|
+
'font-size' => options[:theme].title_font_size || relative(100),
|
11
11
|
'font-family' => options[:theme].font_family,
|
12
12
|
:fill => options[:theme].marker,
|
13
13
|
:stroke => 'none', 'stroke-width' => '0',
|
@@ -1,31 +1,24 @@
|
|
1
1
|
module Scruffy
|
2
2
|
module Components
|
3
3
|
class ValueMarkers < Base
|
4
|
-
attr_accessor :markers
|
5
4
|
|
5
|
+
include Scruffy::Helpers::Marker
|
6
|
+
|
7
|
+
attr_accessor :markers
|
8
|
+
|
6
9
|
def draw(svg, bounds, options={})
|
7
10
|
markers = (options[:markers] || self.markers) || 5
|
8
|
-
all_values = []
|
9
|
-
|
10
|
-
(0...markers).each do |idx|
|
11
|
-
marker = ((1 / (markers - 1).to_f) * idx) * bounds[:height]
|
12
|
-
all_values << (options[:max_value] - options[:min_value]) * ((1 / (markers - 1).to_f) * idx) + options[:min_value]
|
13
|
-
end
|
14
11
|
|
15
|
-
(
|
16
|
-
marker = ((1 / (markers - 1).to_f) * idx) * bounds[:height]
|
17
|
-
marker_value = (options[:max_value] - options[:min_value]) * ((1 / (markers - 1).to_f) * idx) + options[:min_value]
|
18
|
-
marker_value = options[:value_formatter].route_format(marker_value, idx, options.merge({:all_values => all_values})) if options[:value_formatter]
|
12
|
+
each_marker(markers, options[:min_value], options[:max_value], bounds[:height], options, :value_formatter) do |label, y|
|
19
13
|
|
20
|
-
svg.text(
|
14
|
+
svg.text( label,
|
21
15
|
:x => bounds[:width],
|
22
|
-
:y => (bounds[:height] -
|
23
|
-
'font-size' => relative(8),
|
16
|
+
:y => (bounds[:height] - y),
|
17
|
+
'font-size' => options[:theme].marker_font_size || relative(8),
|
24
18
|
'font-family' => options[:theme].font_family,
|
25
|
-
:fill => ((options
|
19
|
+
:fill => ((options[:marker_color_override] || options[:theme].marker) || 'white').to_s,
|
26
20
|
'text-anchor' => 'end')
|
27
21
|
end
|
28
|
-
|
29
22
|
end
|
30
23
|
end
|
31
24
|
end
|
data/lib/scruffy/formatters.rb
CHANGED
@@ -81,9 +81,11 @@ module Scruffy::Formatters
|
|
81
81
|
#
|
82
82
|
# separator:: decimal separator. Defaults to '.'
|
83
83
|
# delimiter:: delimiter character. Defaults to ','
|
84
|
-
# precision_limit:: upper limit for auto precision.
|
84
|
+
# precision_limit:: upper limit for auto precision. (Ignored if roundup is specified)
|
85
|
+
# roundup:: round up the number to the given interval
|
85
86
|
def initialize(options = {})
|
86
87
|
@precision = options[:precision] || :none
|
88
|
+
@roundup = options[:roundup] || :none
|
87
89
|
@separator = options[:separator] || '.'
|
88
90
|
@delimiter = options[:delimiter] || ','
|
89
91
|
@precision_limit = options[:precision_limit] || 4
|
@@ -107,16 +109,32 @@ module Scruffy::Formatters
|
|
107
109
|
my_separator = @separator
|
108
110
|
my_separator = "" unless my_precision > 0
|
109
111
|
begin
|
110
|
-
|
112
|
+
number = ""
|
113
|
+
|
114
|
+
if @roundup == :none
|
115
|
+
parts = number_with_precision(target, my_precision).split('.')
|
116
|
+
number = parts[0].to_s.gsub(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{@delimiter}") + my_separator + parts[1].to_s
|
117
|
+
else
|
118
|
+
number = roundup(target.to_f, @roundup).to_i.to_s
|
119
|
+
end
|
111
120
|
|
112
|
-
number = parts[0].to_s.gsub(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{@delimiter}") + my_separator + parts[1].to_s
|
113
121
|
number
|
114
122
|
rescue StandardError => e
|
115
123
|
target
|
116
124
|
end
|
117
125
|
end
|
126
|
+
|
127
|
+
|
128
|
+
def roundup(target, nearest=100)
|
129
|
+
target % nearest == 0 ? target : target + nearest - (target % nearest)
|
130
|
+
end
|
131
|
+
def rounddown(target, nearest=100)
|
132
|
+
target % nearest == 0 ? target : target - (target % nearest)
|
133
|
+
end
|
118
134
|
end
|
119
135
|
|
136
|
+
|
137
|
+
|
120
138
|
# Currency formatter.
|
121
139
|
#
|
122
140
|
# Provides formatting for currencies.
|
@@ -150,9 +168,12 @@ module Scruffy::Formatters
|
|
150
168
|
number = "(" + @unit + parts[0].to_i.abs.to_s.gsub(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{@delimiter}") + @separator + parts[1].to_s + ")"
|
151
169
|
else
|
152
170
|
number = @unit + parts[0].to_s.gsub(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{@delimiter}") + @separator + parts[1].to_s
|
171
|
+
number.gsub!(@unit + '-', '-' + @unit)
|
153
172
|
end
|
154
173
|
if (target.to_f < 0) && @negative_color
|
155
174
|
options[:marker_color_override] = @negative_color
|
175
|
+
else
|
176
|
+
options[:marker_color_override] = nil
|
156
177
|
end
|
157
178
|
number
|
158
179
|
rescue
|
@@ -191,5 +212,22 @@ module Scruffy::Formatters
|
|
191
212
|
end
|
192
213
|
end
|
193
214
|
end
|
215
|
+
|
216
|
+
|
217
|
+
class Date < Base
|
218
|
+
|
219
|
+
def initialize(format_string, options = {})
|
220
|
+
@format_string = format_string
|
221
|
+
end
|
222
|
+
|
223
|
+
# Formats percentages.
|
224
|
+
def format(target, idx, options)
|
225
|
+
begin
|
226
|
+
target.strftime(@format_string)
|
227
|
+
rescue
|
228
|
+
target
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
194
232
|
|
195
233
|
end
|
data/lib/scruffy/graph.rb
CHANGED
@@ -74,16 +74,18 @@ module Scruffy
|
|
74
74
|
# doesn't necessarily mean they will make any logical sense together. We leave those decisions up to you. :)
|
75
75
|
|
76
76
|
class Graph
|
77
|
-
extend Forwardable
|
77
|
+
extend Forwardable
|
78
78
|
|
79
79
|
include Scruffy::Helpers::LayerContainer
|
80
80
|
|
81
81
|
# Delegating these getters to the internal state object.
|
82
|
-
def_delegators :internal_state, :title, :theme, :default_type,
|
83
|
-
:point_markers, :value_formatter, :rasterizer
|
82
|
+
def_delegators :internal_state, :title,:x_legend,:y_legend, :theme, :default_type,
|
83
|
+
:point_markers,:point_markers_rotation,:point_markers_ticks, :value_formatter, :rasterizer,
|
84
|
+
:key_formatter
|
84
85
|
|
85
|
-
def_delegators :internal_state, :title=, :theme=, :default_type=,
|
86
|
-
:point_markers=, :value_formatter=, :rasterizer
|
86
|
+
def_delegators :internal_state, :title=, :theme=,:x_legend=,:y_legend=, :default_type=,
|
87
|
+
:point_markers=,:point_markers_rotation=,:point_markers_ticks=, :value_formatter=, :rasterizer=,
|
88
|
+
:key_formatter=
|
87
89
|
|
88
90
|
attr_reader :renderer # Writer defined below
|
89
91
|
|
@@ -96,11 +98,15 @@ module Scruffy
|
|
96
98
|
# Options:
|
97
99
|
#
|
98
100
|
# title:: Graph's title
|
101
|
+
# x_legend :: Title for X Axis
|
102
|
+
# y_legend :: Title for Y Axis
|
99
103
|
# theme:: A theme object to use when rendering graph
|
100
104
|
# layers:: An array of Layers for this graph to use
|
101
105
|
# default_type:: A symbol indicating the default type of Layer for this graph
|
102
106
|
# value_formatter:: Sets a formatter used to modify marker values prior to rendering
|
103
107
|
# point_markers:: Sets the x-axis marker values
|
108
|
+
# point_markers_rotation:: Sets the angle of rotation for x-axis marker values
|
109
|
+
# point_markers_ticks:: Sets a small tick mark above each marker value. Helful when used with rotation.
|
104
110
|
# rasterizer:: Sets the rasterizer to use when rendering to an image format. Defaults to RMagick.
|
105
111
|
def initialize(*args)
|
106
112
|
self.default_type = args.shift if args.first.is_a?(Symbol)
|
@@ -108,12 +114,15 @@ module Scruffy
|
|
108
114
|
raise ArgumentError, "The arguments provided are not supported." if args.size > 0
|
109
115
|
|
110
116
|
options ||= {}
|
111
|
-
|
117
|
+
|
118
|
+
|
119
|
+
self.theme = Scruffy::Themes::Standard.new
|
112
120
|
self.renderer = Scruffy::Renderers::Standard.new
|
113
|
-
self.rasterizer = Scruffy::Rasterizers::
|
121
|
+
self.rasterizer = Scruffy::Rasterizers::MiniMagickRasterizer.new
|
114
122
|
self.value_formatter = Scruffy::Formatters::Number.new
|
123
|
+
self.key_formatter = Scruffy::Formatters::Number.new
|
115
124
|
|
116
|
-
%w(title theme layers default_type value_formatter point_markers rasterizer).each do |arg|
|
125
|
+
%w(title x_legend y_legend theme layers default_type value_formatter point_markers point_markers_rotation point_markers_ticks rasterizer key_formatter).each do |arg|
|
117
126
|
self.send("#{arg}=".to_sym, options.delete(arg.to_sym)) unless options[arg.to_sym].nil?
|
118
127
|
end
|
119
128
|
|
@@ -128,6 +137,7 @@ module Scruffy
|
|
128
137
|
# theme:: Theme used to render graph for this render only.
|
129
138
|
# min_value:: Overrides the calculated minimum value used for the graph.
|
130
139
|
# max_value:: Overrides the calculated maximum value used for the graph.
|
140
|
+
# renderer:: Provide a Renderer object to use instead of the default.
|
131
141
|
#
|
132
142
|
# For other image formats:
|
133
143
|
# as:: File format to render to ('PNG', 'JPG', etc)
|
@@ -135,14 +145,20 @@ module Scruffy
|
|
135
145
|
def render(options = {})
|
136
146
|
options[:theme] ||= theme
|
137
147
|
options[:value_formatter] ||= value_formatter
|
148
|
+
options[:key_formatter] ||= key_formatter
|
138
149
|
options[:point_markers] ||= point_markers
|
150
|
+
options[:point_markers_rotation] ||= point_markers_rotation
|
151
|
+
options[:point_markers_ticks] ||= point_markers_ticks
|
139
152
|
options[:size] ||= (options[:width] ? [options[:width], (options.delete(:width) * 0.6).to_i] : [600, 360])
|
140
153
|
options[:title] ||= title
|
154
|
+
options[:x_legend] ||= x_legend
|
155
|
+
options[:y_legend] ||= y_legend
|
141
156
|
options[:layers] ||= layers
|
142
|
-
options[:min_value] ||= bottom_value(:
|
143
|
-
options[:max_value] ||= top_value
|
157
|
+
options[:min_value] ||= bottom_value(options[:padding] ? options[:padding] : nil)
|
158
|
+
options[:max_value] ||= top_value(options[:padding] ? options[:padding] : nil)
|
159
|
+
options[:min_key] ||= bottom_key
|
160
|
+
options[:max_key] ||= top_key
|
144
161
|
options[:graph] ||= self
|
145
|
-
|
146
162
|
|
147
163
|
# Removed for now.
|
148
164
|
# Added for making smaller fonts more legible, but may not be needed after all.
|
data/lib/scruffy/graph_state.rb
CHANGED
@@ -11,10 +11,15 @@ module Scruffy
|
|
11
11
|
class GraphState
|
12
12
|
|
13
13
|
attr_accessor :title
|
14
|
+
attr_accessor :x_legend
|
15
|
+
attr_accessor :y_legend
|
14
16
|
attr_accessor :theme
|
15
17
|
attr_accessor :default_type
|
16
18
|
attr_accessor :point_markers
|
19
|
+
attr_accessor :point_markers_rotation
|
20
|
+
attr_accessor :point_markers_ticks
|
17
21
|
attr_accessor :value_formatter
|
22
|
+
attr_accessor :key_formatter
|
18
23
|
attr_accessor :rasterizer
|
19
24
|
|
20
25
|
def initialize
|
data/lib/scruffy/helpers.rb
CHANGED
@@ -67,7 +67,9 @@ module Scruffy::Helpers
|
|
67
67
|
# If padding is set to :padded, a 15% padding is added to the highest value.
|
68
68
|
def top_value(padding=nil) # :nodoc:
|
69
69
|
topval = layers.inject(0) { |max, layer| (max = ((max < layer.top_value) ? layer.top_value : max)) unless layer.top_value.nil?; max }
|
70
|
-
|
70
|
+
below_zero = (topval <= 0)
|
71
|
+
topval = padding == :padded ? (topval + ((topval - bottom_value) * 0.15)) : topval
|
72
|
+
(below_zero && topval > 0) ? 0 : topval
|
71
73
|
end
|
72
74
|
|
73
75
|
# Returns the lowest value in any of this container's layers.
|
@@ -76,15 +78,37 @@ module Scruffy::Helpers
|
|
76
78
|
# If the lowest value is greater than zero, then the padding will not cross the zero line, preventing
|
77
79
|
# negative values from being introduced into the graph purely due to padding.
|
78
80
|
def bottom_value(padding=nil) # :nodoc:
|
79
|
-
botval = layers.inject(
|
80
|
-
|
81
|
-
|
81
|
+
botval = layers.inject(0) do |min, layer|
|
82
|
+
(min = ((min > layer.bottom_value) ? layer.bottom_value : min)) unless layer.bottom_value.nil?
|
83
|
+
min
|
84
|
+
end
|
85
|
+
above_zero = (botval >= 0)
|
86
|
+
botval = (botval - ((top_value - botval) * 0.15)) if padding == :padded
|
82
87
|
|
83
88
|
# Don't introduce negative values solely due to padding.
|
84
89
|
# A user-provided value must be negative before padding will extend into negative values.
|
85
90
|
(above_zero && botval < 0) ? 0 : botval
|
86
91
|
end
|
87
92
|
|
93
|
+
def bottom_key(padding=nil)
|
94
|
+
return 0 unless layers.any?
|
95
|
+
min = layers[0].bottom_key
|
96
|
+
layers.each do |layer|
|
97
|
+
min = layer.bottom_key if min.nil? && !layer.bottom_key.nil?
|
98
|
+
(min = ((min > layer.bottom_key) ? layer.bottom_key : min)) unless layer.bottom_key.nil?
|
99
|
+
end
|
100
|
+
min
|
101
|
+
end
|
102
|
+
|
103
|
+
def top_key(padding=nil)
|
104
|
+
return 1 unless layers.any?
|
105
|
+
max = layers[0].top_key
|
106
|
+
layers.each do |layer|
|
107
|
+
max = layer.top_key if max.nil? && !layer.top_key.nil?
|
108
|
+
(max = ((max < layer.top_key) ? layer.top_key : max)) unless layer.top_key.nil?
|
109
|
+
end
|
110
|
+
max
|
111
|
+
end
|
88
112
|
|
89
113
|
protected
|
90
114
|
def to_camelcase(type) # :nodoc:
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Scruffy::Helpers
|
2
|
+
|
3
|
+
module Marker
|
4
|
+
|
5
|
+
def each_marker(markers, min, max, width, options, format_key)
|
6
|
+
all_values = []
|
7
|
+
|
8
|
+
(0...markers).each do |idx|
|
9
|
+
percent = idx.to_f / (markers-1)
|
10
|
+
all_values << min + (max - min) * percent
|
11
|
+
end
|
12
|
+
|
13
|
+
all_values.size.times do |idx|
|
14
|
+
dx = width/(markers - 1)
|
15
|
+
|
16
|
+
location = idx.to_f * dx #+ dx/2
|
17
|
+
marker_value = all_values[idx]
|
18
|
+
marker_value = options[format_key].route_format(marker_value, idx, options.merge({:all_values => all_values})) if options[format_key]
|
19
|
+
|
20
|
+
yield marker_value, location
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -8,27 +8,27 @@ module Scruffy::Helpers
|
|
8
8
|
# Allows all standard point operations to be called on both Array and Hash
|
9
9
|
module PointContainer
|
10
10
|
def self.extended point_set
|
11
|
-
point_set.extend(const_get(point_set.class.to_s))
|
11
|
+
point_set.extend(const_get("#{point_set.class.to_s}_ext"))
|
12
12
|
end
|
13
13
|
|
14
|
-
def
|
15
|
-
values.find_all { |v| v.respond_to?
|
14
|
+
def sortable(values)
|
15
|
+
values.find_all { |v| v.respond_to?(:<=>) && !v.nil? }
|
16
16
|
end
|
17
17
|
|
18
|
-
def
|
19
|
-
values.find_all { |v| v.respond_to?
|
18
|
+
def summable(values)
|
19
|
+
values.find_all { |v| v.respond_to?(:+) && !v.nil? }
|
20
20
|
end
|
21
21
|
|
22
22
|
def maximum_value
|
23
|
-
|
23
|
+
sortable(values).sort.last
|
24
24
|
end
|
25
25
|
|
26
26
|
def minimum_value
|
27
|
-
|
27
|
+
sortable(values).sort.first
|
28
28
|
end
|
29
29
|
|
30
30
|
def sum
|
31
|
-
|
31
|
+
summable(values).inject(0) { |sum, i| sum += i }
|
32
32
|
end
|
33
33
|
|
34
34
|
def inject_with_index memo
|
@@ -40,24 +40,53 @@ module Scruffy::Helpers
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
-
|
43
|
+
def minimum_key
|
44
|
+
sortable(keys).sort.first
|
45
|
+
end
|
46
|
+
|
47
|
+
def maximum_key
|
48
|
+
sortable(keys).sort.last
|
49
|
+
end
|
50
|
+
|
51
|
+
module Array_ext
|
44
52
|
def values
|
45
|
-
self
|
53
|
+
return self unless is_coordinate_list?
|
54
|
+
collect { |x,y| y}
|
55
|
+
end
|
56
|
+
|
57
|
+
def keys
|
58
|
+
return [0,size-1] unless is_coordinate_list?
|
59
|
+
collect { |x,y| x}
|
60
|
+
end
|
61
|
+
|
62
|
+
def is_coordinate_list?
|
63
|
+
if any? && first.is_a?(Array) && first.size == 2
|
64
|
+
return true
|
65
|
+
end
|
66
|
+
return false
|
67
|
+
end
|
68
|
+
|
69
|
+
def each_point(&block)
|
70
|
+
if is_coordinate_list?
|
71
|
+
each{|x,y|block.call(x,y)}
|
72
|
+
else
|
73
|
+
size.times{|k|block.call(k,self[k])}
|
74
|
+
end
|
46
75
|
end
|
47
76
|
end
|
48
77
|
|
49
|
-
module
|
50
|
-
def
|
51
|
-
|
78
|
+
module Hash_ext
|
79
|
+
def is_coordinate_list?
|
80
|
+
true
|
52
81
|
end
|
53
82
|
|
54
|
-
def
|
55
|
-
|
83
|
+
def each_point(&block)
|
84
|
+
keys.sort.each{|k|block.call(k,self[k])}
|
56
85
|
end
|
57
86
|
|
58
87
|
def inject memo
|
59
|
-
|
60
|
-
memo = yield memo, self[
|
88
|
+
keys.sort.each do |k|
|
89
|
+
memo = yield memo, self[k]
|
61
90
|
end
|
62
91
|
memo
|
63
92
|
end
|
data/lib/scruffy/layers.rb
CHANGED
@@ -15,10 +15,15 @@ end
|
|
15
15
|
|
16
16
|
require 'scruffy/layers/base'
|
17
17
|
require 'scruffy/layers/area'
|
18
|
+
require 'scruffy/layers/multi_area'
|
18
19
|
require 'scruffy/layers/all_smiles'
|
19
20
|
require 'scruffy/layers/bar'
|
21
|
+
require 'scruffy/layers/box'
|
20
22
|
require 'scruffy/layers/line'
|
21
23
|
require 'scruffy/layers/average'
|
22
24
|
require 'scruffy/layers/stacked'
|
25
|
+
require 'scruffy/layers/multi'
|
26
|
+
require 'scruffy/layers/multi_bar'
|
23
27
|
require 'scruffy/layers/pie'
|
24
|
-
require 'scruffy/layers/pie_slice'
|
28
|
+
require 'scruffy/layers/pie_slice'
|
29
|
+
require 'scruffy/layers/scatter'
|
data/lib/scruffy/layers/bar.rb
CHANGED
@@ -8,20 +8,36 @@ module Scruffy::Layers
|
|
8
8
|
class Bar < Base
|
9
9
|
|
10
10
|
# Draw bar graph.
|
11
|
+
# Now handles positive and negative values gracefully.
|
11
12
|
def draw(svg, coords, options = {})
|
12
|
-
coords.
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
13
|
+
coords.each_with_index do |coord,idx|
|
14
|
+
next if coord.nil?
|
15
|
+
x, y, bar_height = (coord.first), coord.last, 1#(height - coord.last)
|
16
|
+
|
17
|
+
valh = max_value + min_value * -1 #value_height
|
18
|
+
maxh = max_value * height / valh #positive area height
|
19
|
+
minh = min_value * height / valh #negative area height
|
20
|
+
#puts "height = #{height} and max_value = #{max_value} and min_value = #{min_value} and y = #{y} and point = #{points[idx]}"
|
21
|
+
if points[idx] > 0
|
22
|
+
bar_height = points[idx]*maxh/max_value
|
23
|
+
else
|
24
|
+
bar_height = points[idx]*minh/min_value
|
25
|
+
end
|
26
|
+
|
27
|
+
#puts " y = #{y} and point = #{points[idx]}"
|
28
|
+
unless options[:border] == false
|
29
|
+
svg.g(:transform => "translate(-#{relative(0.5)}, -#{relative(0.5)})") {
|
30
|
+
svg.rect( :x => x, :y => y, :width => @bar_width + relative(1), :height => bar_height + relative(1),
|
31
|
+
:style => "fill: black; fill-opacity: 0.15; stroke: none;" )
|
32
|
+
svg.rect( :x => x+relative(0.5), :y => y+relative(2), :width => @bar_width + relative(1), :height => bar_height - relative(0.5),
|
33
|
+
:style => "fill: black; fill-opacity: 0.15; stroke: none;" )
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
current_colour = color.is_a?(Array) ? color[idx % color.size] : color
|
22
38
|
|
23
39
|
svg.rect( :x => x, :y => y, :width => @bar_width, :height => bar_height,
|
24
|
-
|
40
|
+
:fill => current_colour.to_s, 'style' => "opacity: #{opacity}; stroke: none;" )
|
25
41
|
end
|
26
42
|
end
|
27
43
|
|
@@ -34,13 +50,18 @@ module Scruffy::Layers
|
|
34
50
|
# Unfortunately this just mean that bar-graphs and most other graphs
|
35
51
|
# end up on different points. Maybe adding a padding to the coordinates
|
36
52
|
# should be a graph-wide thing?
|
53
|
+
#
|
54
|
+
# Update : x-axis coords for lines and area charts should now line
|
55
|
+
# up with the center of bar charts.
|
56
|
+
|
37
57
|
def generate_coordinates(options = {})
|
38
|
-
@bar_width = (width / points.size) * 0.
|
58
|
+
@bar_width = (width / points.size) * 0.95
|
39
59
|
options[:point_distance] = (width - (width / points.size)) / (points.size - 1).to_f
|
40
60
|
|
41
61
|
#TODO more array work with index, try to rework to be accepting of hashes
|
42
|
-
coords = (0...points.size).map do |idx|
|
43
|
-
|
62
|
+
coords = (0...points.size).map do |idx|
|
63
|
+
next if points[idx].nil?
|
64
|
+
x_coord = (options[:point_distance] * idx) + (width / points.size * 0.5) - (@bar_width * 0.5)
|
44
65
|
|
45
66
|
relative_percent = ((points[idx] == min_value) ? 0 : ((points[idx] - min_value) / (max_value - min_value).to_f))
|
46
67
|
y_coord = (height - (height * relative_percent))
|