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.
Files changed (96) hide show
  1. data/{History.txt → CHANGES.txt} +15 -12
  2. data/README.txt +25 -25
  3. data/lib/scruffy.rb +0 -5
  4. data/lib/scruffy/components.rb +1 -0
  5. data/lib/scruffy/components/axes.rb +23 -0
  6. data/lib/scruffy/components/background.rb +3 -3
  7. data/lib/scruffy/components/base.rb +3 -0
  8. data/lib/scruffy/components/data_markers.rb +23 -8
  9. data/lib/scruffy/components/graphs.rb +4 -0
  10. data/lib/scruffy/components/grid.rb +45 -4
  11. data/lib/scruffy/components/legend.rb +63 -21
  12. data/lib/scruffy/components/title.rb +1 -1
  13. data/lib/scruffy/components/value_markers.rb +9 -16
  14. data/lib/scruffy/formatters.rb +41 -3
  15. data/lib/scruffy/graph.rb +27 -11
  16. data/lib/scruffy/graph_state.rb +5 -0
  17. data/lib/scruffy/helpers.rb +1 -0
  18. data/lib/scruffy/helpers/layer_container.rb +28 -4
  19. data/lib/scruffy/helpers/marker_helper.rb +25 -0
  20. data/lib/scruffy/helpers/point_container.rb +46 -17
  21. data/lib/scruffy/layers.rb +6 -1
  22. data/lib/scruffy/layers/bar.rb +35 -14
  23. data/lib/scruffy/layers/base.rb +51 -21
  24. data/lib/scruffy/layers/box.rb +114 -0
  25. data/lib/scruffy/layers/line.rb +31 -14
  26. data/lib/scruffy/layers/multi.rb +74 -0
  27. data/lib/scruffy/layers/multi_area.rb +119 -0
  28. data/lib/scruffy/layers/multi_bar.rb +51 -0
  29. data/lib/scruffy/layers/scatter.rb +13 -5
  30. data/lib/scruffy/layers/stacked.rb +2 -1
  31. data/lib/scruffy/rasterizers.rb +1 -0
  32. data/lib/scruffy/rasterizers/mini_magick_rasterizer.rb +24 -0
  33. data/lib/scruffy/rasterizers/rmagick_rasterizer.rb +8 -2
  34. data/lib/scruffy/renderers.rb +2 -0
  35. data/lib/scruffy/renderers/axis_legend.rb +41 -0
  36. data/lib/scruffy/renderers/base.rb +6 -4
  37. data/lib/scruffy/renderers/basic.rb +20 -0
  38. data/lib/scruffy/renderers/standard.rb +7 -6
  39. data/lib/scruffy/themes.rb +37 -1
  40. data/lib/scruffy/version.rb +1 -7
  41. data/spec/output/array.svg +55 -0
  42. data/spec/output/hash.svg +55 -0
  43. data/spec/scruffy/graph_spec.rb +4 -4
  44. data/spec/scruffy/layers/base_spec.rb +15 -10
  45. metadata +84 -96
  46. data/CHANGES +0 -104
  47. data/License.txt +0 -20
  48. data/MIT-LICENSE +0 -20
  49. data/Manifest.txt +0 -104
  50. data/PostInstall.txt +0 -6
  51. data/README +0 -9
  52. data/Rakefile +0 -108
  53. data/config/hoe.rb +0 -78
  54. data/config/requirements.rb +0 -15
  55. data/script/console +0 -10
  56. data/script/destroy +0 -14
  57. data/script/generate +0 -14
  58. data/script/txt2html +0 -82
  59. data/setup.rb +0 -1585
  60. data/spec/scruffy/layers/line_spec.rb +0 -10
  61. data/tasks/deployment.rake +0 -34
  62. data/tasks/environment.rake +0 -7
  63. data/tasks/website.rake +0 -17
  64. data/test/graph_creation_test.rb +0 -101
  65. data/test/test_helper.rb +0 -2
  66. data/website/images/blank.gif.html +0 -7
  67. data/website/images/graphs/all_smiles.png +0 -0
  68. data/website/images/graphs/bar_test.png +0 -0
  69. data/website/images/graphs/bar_test.svg +0 -71
  70. data/website/images/graphs/line_test.png +0 -0
  71. data/website/images/graphs/line_test.svg +0 -60
  72. data/website/images/graphs/multi_test.png +0 -0
  73. data/website/images/graphs/multi_test.svg +0 -296
  74. data/website/images/graphs/pie_test.png +0 -0
  75. data/website/images/graphs/pie_test.svg +0 -40
  76. data/website/images/graphs/split_test.png +0 -0
  77. data/website/images/graphs/split_test.svg +0 -295
  78. data/website/images/graphs/stacking_test.png +0 -0
  79. data/website/images/graphs/stacking_test.svg +0 -146
  80. data/website/images/header.png +0 -0
  81. data/website/images/header_gradient.png +0 -0
  82. data/website/images/overlay.png +0 -0
  83. data/website/images/scruffy.png +0 -0
  84. data/website/index.html +0 -225
  85. data/website/index.txt +0 -204
  86. data/website/javascripts/application.js +0 -2
  87. data/website/javascripts/controls.js +0 -815
  88. data/website/javascripts/dragdrop.js +0 -913
  89. data/website/javascripts/effects.js +0 -958
  90. data/website/javascripts/lightbox.js +0 -437
  91. data/website/javascripts/prototype.js +0 -2006
  92. data/website/javascripts/rounded_corners_lite.inc.js +0 -285
  93. data/website/stylesheets/lightbox.css +0 -27
  94. data/website/stylesheets/screen.css +0 -147
  95. data/website/stylesheets/scruffy.css +0 -227
  96. 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
- (0...markers).each do |idx|
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( marker_value.to_s,
14
+ svg.text( label,
21
15
  :x => bounds[:width],
22
- :y => (bounds[:height] - marker),
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.delete(:marker_color_override) || options[:theme].marker) || 'white').to_s,
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
@@ -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
- parts = number_with_precision(target, my_precision).split('.')
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
@@ -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
- self.theme = Scruffy::Themes::Keynote.new
117
+
118
+
119
+ self.theme = Scruffy::Themes::Standard.new
112
120
  self.renderer = Scruffy::Renderers::Standard.new
113
- self.rasterizer = Scruffy::Rasterizers::RMagickRasterizer.new
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(:padded)
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.
@@ -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
@@ -9,4 +9,5 @@ module Scruffy::Helpers; end
9
9
  require 'scruffy/helpers/canvas'
10
10
  require 'scruffy/helpers/layer_container'
11
11
  require 'scruffy/helpers/point_container'
12
+ require 'scruffy/helpers/marker_helper'
12
13
  #require 'scruffy/helpers/meta'
@@ -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
- padding == :padded ? (topval - ((topval - bottom_value) * 0.15)) : topval
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(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))
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 sortable_values
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 summable_values
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
- sortable_values.sort.last
23
+ sortable(values).sort.last
24
24
  end
25
25
 
26
26
  def minimum_value
27
- sortable_values.sort.first
27
+ sortable(values).sort.first
28
28
  end
29
29
 
30
30
  def sum
31
- summable_values.inject(0) { |sum, i| sum += i }
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
- module Array
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 Hash
50
- def minimum_key
51
- self.keys.sort.first
78
+ module Hash_ext
79
+ def is_coordinate_list?
80
+ true
52
81
  end
53
82
 
54
- def maximum_key
55
- self.keys.sort.last
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
- (minimum_key..maximum_key).each do |i|
60
- memo = yield memo, self[i]
88
+ keys.sort.each do |k|
89
+ memo = yield memo, self[k]
61
90
  end
62
91
  memo
63
92
  end
@@ -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'
@@ -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.each do |coord|
13
- x, y, bar_height = (coord.first-(@bar_width * 0.5)), coord.last, (height - coord.last)
14
-
15
- svg.g(:transform => "translate(-#{relative(0.5)}, -#{relative(0.5)})") {
16
- svg.rect( :x => x, :y => y, :width => @bar_width + relative(1), :height => bar_height + relative(1),
17
- :style => "fill: black; fill-opacity: 0.15; stroke: none;" )
18
- svg.rect( :x => x+relative(0.5), :y => y+relative(2), :width => @bar_width + relative(1), :height => bar_height - relative(0.5),
19
- :style => "fill: black; fill-opacity: 0.15; stroke: none;" )
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
- :fill => color.to_s, 'style' => "opacity: #{opacity}; stroke: none;" )
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.9
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
- x_coord = (options[:point_distance] * idx) + (width / points.size * 0.5)
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))