scruffy 0.2.6 → 0.3.0.beta1
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/{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))
|