gruff 0.8.0 → 0.9.0

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 (88) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE.md +18 -0
  3. data/.gitignore +3 -0
  4. data/.rubocop.yml +93 -0
  5. data/.rubocop_todo.yml +23 -810
  6. data/.travis.yml +4 -4
  7. data/.yardopts +1 -0
  8. data/CHANGELOG.md +22 -0
  9. data/Gemfile +3 -1
  10. data/README.md +44 -21
  11. data/Rakefile +2 -206
  12. data/docker/Dockerfile +14 -0
  13. data/docker/build.sh +4 -0
  14. data/docker/launch.sh +4 -0
  15. data/gruff.gemspec +11 -8
  16. data/init.rb +2 -0
  17. data/lib/gruff.rb +23 -0
  18. data/lib/gruff/accumulator_bar.rb +6 -6
  19. data/lib/gruff/area.rb +13 -17
  20. data/lib/gruff/bar.rb +58 -41
  21. data/lib/gruff/base.rb +243 -566
  22. data/lib/gruff/bezier.rb +12 -14
  23. data/lib/gruff/bullet.rb +39 -57
  24. data/lib/gruff/dot.rb +25 -59
  25. data/lib/gruff/{bar_conversion.rb → helper/bar_conversion.rb} +13 -12
  26. data/lib/gruff/helper/bar_value_label_mixin.rb +30 -0
  27. data/lib/gruff/{stacked_mixin.rb → helper/stacked_mixin.rb} +7 -6
  28. data/lib/gruff/line.rb +95 -177
  29. data/lib/gruff/mini/bar.rb +6 -7
  30. data/lib/gruff/mini/legend.rb +16 -32
  31. data/lib/gruff/mini/pie.rb +6 -7
  32. data/lib/gruff/mini/side_bar.rb +4 -5
  33. data/lib/gruff/net.rb +37 -65
  34. data/lib/gruff/patch/rmagick.rb +33 -0
  35. data/lib/gruff/patch/string.rb +8 -0
  36. data/lib/gruff/photo_bar.rb +19 -19
  37. data/lib/gruff/pie.rb +22 -73
  38. data/lib/gruff/renderer/bezier.rb +21 -0
  39. data/lib/gruff/renderer/circle.rb +21 -0
  40. data/lib/gruff/renderer/dash_line.rb +22 -0
  41. data/lib/gruff/renderer/dot.rb +39 -0
  42. data/lib/gruff/renderer/ellipse.rb +21 -0
  43. data/lib/gruff/renderer/line.rb +34 -0
  44. data/lib/gruff/renderer/polygon.rb +23 -0
  45. data/lib/gruff/renderer/polyline.rb +21 -0
  46. data/lib/gruff/renderer/rectangle.rb +19 -0
  47. data/lib/gruff/renderer/renderer.rb +127 -0
  48. data/lib/gruff/renderer/text.rb +42 -0
  49. data/lib/gruff/scatter.rb +85 -156
  50. data/lib/gruff/scene.rb +22 -30
  51. data/lib/gruff/side_bar.rb +62 -58
  52. data/lib/gruff/side_stacked_bar.rb +47 -43
  53. data/lib/gruff/spider.rb +19 -36
  54. data/lib/gruff/stacked_area.rb +17 -21
  55. data/lib/gruff/stacked_bar.rb +50 -24
  56. data/lib/gruff/store/base_data.rb +34 -0
  57. data/lib/gruff/store/custom_data.rb +34 -0
  58. data/lib/gruff/store/store.rb +80 -0
  59. data/lib/gruff/store/xy_data.rb +55 -0
  60. data/lib/gruff/themes.rb +3 -3
  61. data/lib/gruff/version.rb +3 -1
  62. metadata +41 -30
  63. data/Manifest.txt +0 -81
  64. data/assets/bubble.png +0 -0
  65. data/assets/city_scene/background/0000.png +0 -0
  66. data/assets/city_scene/background/0600.png +0 -0
  67. data/assets/city_scene/background/2000.png +0 -0
  68. data/assets/city_scene/clouds/cloudy.png +0 -0
  69. data/assets/city_scene/clouds/partly_cloudy.png +0 -0
  70. data/assets/city_scene/clouds/stormy.png +0 -0
  71. data/assets/city_scene/grass/default.png +0 -0
  72. data/assets/city_scene/haze/true.png +0 -0
  73. data/assets/city_scene/number_sample/1.png +0 -0
  74. data/assets/city_scene/number_sample/2.png +0 -0
  75. data/assets/city_scene/number_sample/default.png +0 -0
  76. data/assets/city_scene/sky/0000.png +0 -0
  77. data/assets/city_scene/sky/0200.png +0 -0
  78. data/assets/city_scene/sky/0400.png +0 -0
  79. data/assets/city_scene/sky/0600.png +0 -0
  80. data/assets/city_scene/sky/0800.png +0 -0
  81. data/assets/city_scene/sky/1000.png +0 -0
  82. data/assets/city_scene/sky/1200.png +0 -0
  83. data/assets/city_scene/sky/1400.png +0 -0
  84. data/assets/city_scene/sky/1500.png +0 -0
  85. data/assets/city_scene/sky/1700.png +0 -0
  86. data/assets/city_scene/sky/2000.png +0 -0
  87. data/assets/pc306715.jpg +0 -0
  88. data/lib/gruff/deprecated.rb +0 -38
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gruff
4
+ class Renderer::Text
5
+ def initialize(text, args = {})
6
+ @text = text.to_s
7
+ @font = args[:font]
8
+ @font_size = args[:size]
9
+ @font_color = args[:color]
10
+ @font_weight = args[:weight] || Magick::NormalWeight
11
+ @rotation = args[:rotation]
12
+ end
13
+
14
+ def render(width, height, x, y, gravity = Magick::NorthGravity)
15
+ draw = Renderer.instance.draw
16
+ image = Renderer.instance.image
17
+ scale = Renderer.instance.scale
18
+
19
+ draw.rotation = @rotation if @rotation
20
+ draw.fill = @font_color
21
+ draw.stroke = 'transparent'
22
+ draw.font = @font if @font
23
+ draw.font_weight = @font_weight
24
+ draw.pointsize = @font_size * scale
25
+ draw.gravity = gravity
26
+ draw.annotate_scaled(image,
27
+ width, height,
28
+ x, y,
29
+ @text, scale)
30
+ draw.rotation = -@rotation if @rotation
31
+ end
32
+
33
+ def self.metrics(text, size, font_weight = Magick::NormalWeight)
34
+ draw = Renderer.instance.draw
35
+ image = Renderer.instance.image
36
+
37
+ draw.font_weight = font_weight
38
+ draw.pointsize = size
39
+ draw.get_type_metrics(image, text.to_s)
40
+ end
41
+ end
42
+ end
@@ -1,15 +1,15 @@
1
- require File.dirname(__FILE__) + '/base'
1
+ # frozen_string_literal: true
2
+
3
+ require 'gruff/base'
2
4
 
3
5
  # Here's how to set up an XY Scatter Chart
4
6
  #
5
- # g = Gruff::Scatter.new(800)
6
- # g.data(:apples, [1,2,3,4], [4,3,2,1])
7
- # g.data('oranges', [5,7,8], [4,1,7])
8
- # g.write('test/output/scatter.png')
9
- #
7
+ # g = Gruff::Scatter.new(800)
8
+ # g.data(:apples, [1,2,3,4], [4,3,2,1])
9
+ # g.data('oranges', [5,7,8], [4,1,7])
10
+ # g.write('test/output/scatter.png')
10
11
  #
11
12
  class Gruff::Scatter < Gruff::Base
12
-
13
13
  # Maximum X Value. The value will get overwritten by the max in the
14
14
  # datasets.
15
15
  attr_accessor :maximum_x_value
@@ -18,47 +18,29 @@ class Gruff::Scatter < Gruff::Base
18
18
  # datasets.
19
19
  attr_accessor :minimum_x_value
20
20
 
21
- # The number of vertical lines shown for reference
21
+ # The number of vertical lines shown for reference.
22
22
  attr_accessor :marker_x_count
23
23
 
24
- #~ # Draw a dashed horizontal line at the given y value
25
- #~ attr_accessor :baseline_y_value
26
-
27
- #~ # Color of the horizontal baseline
28
- #~ attr_accessor :baseline_y_color
29
-
30
- #~ # Draw a dashed horizontal line at the given y value
31
- #~ attr_accessor :baseline_x_value
32
-
33
- #~ # Color of the horizontal baseline
34
- #~ attr_accessor :baseline_x_color
35
-
36
- # Attributes to allow customising the size of the points
24
+ # Attributes to allow customising the size of the points.
37
25
  attr_accessor :circle_radius
38
26
  attr_accessor :stroke_width
39
27
 
40
- # Allow disabling the significant rounding when labeling the X axis
41
- # This is useful when working with a small range of high values (for example, a date range of months, while seconds as units)
28
+ # Allow disabling the significant rounding when labeling the X axis.
29
+ # This is useful when working with a small range of high values (for example, a date range of months, while seconds as units).
42
30
  attr_accessor :disable_significant_rounding_x_axis
43
31
 
44
- # Allow enabling vertical lines. When you have a lot of data, they can work great
32
+ # Allow enabling vertical lines. When you have a lot of data, they can work great.
45
33
  attr_accessor :enable_vertical_line_markers
46
34
 
47
- # Allow using vertical labels in the X axis (and setting the label margin)
35
+ # Allow using vertical labels in the X axis (and setting the label margin).
48
36
  attr_accessor :x_label_margin
49
37
  attr_accessor :use_vertical_x_labels
50
38
 
51
- # Allow passing lambdas to format labels
39
+ # Allow passing lambdas to format labels.
52
40
  attr_accessor :y_axis_label_format
53
41
  attr_accessor :x_axis_label_format
54
42
 
55
- # Gruff::Scatter takes the same parameters as the Gruff::Line graph
56
- #
57
- # ==== Example
58
- #
59
- # g = Gruff::Scatter.new
60
- #
61
- def initialize(*)
43
+ def initialize_ivars
62
44
  super
63
45
 
64
46
  @baseline_x_color = @baseline_y_color = 'red'
@@ -73,61 +55,33 @@ class Gruff::Scatter < Gruff::Base
73
55
  @x_axis_label_format = nil
74
56
  @x_label_margin = nil
75
57
  @y_axis_label_format = nil
76
- end
77
58
 
78
- def setup_drawing
79
- # TODO Need to get x-axis labels working. Current behavior will be to not allow.
80
- @labels = {}
81
-
82
- super
83
-
84
- # Translate our values so that we can use the base methods for drawing
85
- # the standard chart stuff
86
- @column_count = @x_spread
59
+ @store = Gruff::Store.new(Gruff::Store::XYData)
87
60
  end
61
+ private :initialize_ivars
88
62
 
89
63
  def draw
90
64
  super
91
- return unless @has_data
65
+ return unless data_given?
92
66
 
93
67
  # Check to see if more than one datapoint was given. NaN can result otherwise.
94
- @x_increment = (@column_count > 1) ? (@graph_width / (@column_count - 1).to_f) : @graph_width
68
+ @x_increment = (@x_spread > 1) ? (@graph_width / (@x_spread - 1).to_f) : @graph_width
95
69
 
96
- #~ if (defined?(@norm_y_baseline)) then
97
- #~ level = @graph_top + (@graph_height - @norm_baseline * @graph_height)
98
- #~ @d = @d.push
99
- #~ @d.stroke_color @baseline_color
100
- #~ @d.fill_opacity 0.0
101
- #~ @d.stroke_dasharray(10, 20)
102
- #~ @d.stroke_width 5
103
- #~ @d.line(@graph_left, level, @graph_left + @graph_width, level)
104
- #~ @d = @d.pop
105
- #~ end
106
-
107
- #~ if (defined?(@norm_x_baseline)) then
108
-
109
- #~ end
110
-
111
- @norm_data.each do |data_row|
112
- data_row[DATA_VALUES_INDEX].each_with_index do |data_point, index|
113
- x_value = data_row[DATA_VALUES_X_INDEX][index]
114
- next if data_point.nil? || x_value.nil?
70
+ store.norm_data.each do |data_row|
71
+ data_row.coordinates.each do |x_value, y_value|
72
+ next if y_value.nil? || x_value.nil?
115
73
 
116
74
  new_x = get_x_coord(x_value, @graph_width, @graph_left)
117
- new_y = @graph_top + (@graph_height - data_point * @graph_height)
75
+ new_y = @graph_top + (@graph_height - y_value * @graph_height)
118
76
 
119
77
  # Reset each time to avoid thin-line errors
120
- @d = @d.stroke data_row[DATA_COLOR_INDEX]
121
- @d = @d.fill data_row[DATA_COLOR_INDEX]
122
- @d = @d.stroke_opacity 1.0
123
- @d = @d.stroke_width @stroke_width || clip_value_if_greater_than(@columns / (@norm_data.first[1].size * 4), 5.0)
124
-
125
- circle_radius = @circle_radius || clip_value_if_greater_than(@columns / (@norm_data.first[1].size * 2.5), 5.0)
126
- @d = @d.circle(new_x, new_y, new_x - circle_radius, new_y)
78
+ stroke_width = @stroke_width || clip_value_if_greater_than(@columns / (store.norm_data.first[1].size * 4), 5.0)
79
+ circle_radius = @circle_radius || clip_value_if_greater_than(@columns / (store.norm_data.first[1].size * 2.5), 5.0)
80
+ Gruff::Renderer::Circle.new(color: data_row.color, width: stroke_width).render(new_x, new_y, new_x - circle_radius, new_y)
127
81
  end
128
82
  end
129
83
 
130
- @d.draw(@base_image)
84
+ Gruff::Renderer.finish
131
85
  end
132
86
 
133
87
  # The first parameter is the name of the dataset. The next two are the
@@ -140,57 +94,63 @@ class Gruff::Scatter < Gruff::Base
140
94
  # If the color argument is nil, the next color from the default theme will
141
95
  # be used.
142
96
  #
143
- # NOTE: If you want to use a preset theme, you must set it before calling
144
- # data().
97
+ # @note If you want to use a preset theme, you must set it before calling {#data}.
98
+ #
99
+ # @param name [String, Symbol] containing the name of the dataset.
100
+ # @param x_data_points [Array] An Array of of x-axis data points.
101
+ # @param y_data_points [Array] An Array of of y-axis data points.
102
+ # @param color [String] The hex string for the color of the dataset. Defaults to nil.
145
103
  #
146
- # ==== Parameters
147
- # name:: String or Symbol containing the name of the dataset.
148
- # x_data_points:: An Array of of x-axis data points.
149
- # y_data_points:: An Array of of y-axis data points.
150
- # color:: The hex string for the color of the dataset. Defaults to nil.
151
104
  #
152
- # ==== Exceptions
153
- # Data points contain nil values::
105
+ # @raise [ArgumentError] Data points contain nil values.
154
106
  # This error will get raised if either the x or y axis data points array
155
- # contains a <tt>nil</tt> value. The graph will not make an assumption
156
- # as how to graph <tt>nil</tt>
157
- # x_data_points is empty::
107
+ # contains a +nil+ value. The graph will not make an assumption
108
+ # as how to graph +nil+.
109
+ # @raise [ArgumentError] +x_data_points+ is empty.
158
110
  # This error is raised when the array for the x-axis points are empty
159
- # y_data_points is empty::
160
- # This error is raised when the array for the y-axis points are empty
161
- # x_data_points.length != y_data_points.length::
162
- # Error means that the x and y axis point arrays do not match in length
111
+ # @raise [ArgumentError] +y_data_points+ is empty.
112
+ # This error is raised when the array for the y-axis points are empty.
113
+ # @raise [ArgumentError] +x_data_points.length != y_data_points.length+.
114
+ # Error means that the x and y axis point arrays do not match in length.
163
115
  #
164
- # ==== Examples
165
- # g = Gruff::Scatter.new
166
- # g.data(:apples, [1,2,3], [3,2,1])
167
- # g.data('oranges', [1,1,1], [2,3,4])
168
- # g.data('bitter_melon', [3,5,6], [6,7,8], '#000000')
116
+ # @example
117
+ # g = Gruff::Scatter.new
118
+ # g.data(:apples, [1,2,3], [3,2,1])
119
+ # g.data('oranges', [1,1,1], [2,3,4])
120
+ # g.data('bitter_melon', [3,5,6], [6,7,8], '#000000')
169
121
  #
170
122
  def data(name, x_data_points = [], y_data_points = [], color = nil)
123
+ # make sure it's an array
124
+ x_data_points = Array(x_data_points)
125
+ y_data_points = Array(y_data_points)
126
+
171
127
  raise ArgumentError, 'Data Points contain nil Value!' if x_data_points.include?(nil) || y_data_points.include?(nil)
172
128
  raise ArgumentError, 'x_data_points is empty!' if x_data_points.empty?
173
129
  raise ArgumentError, 'y_data_points is empty!' if y_data_points.empty?
174
130
  raise ArgumentError, 'x_data_points.length != y_data_points.length!' if x_data_points.length != y_data_points.length
175
131
 
176
- # Call the existing data routine for the y axis data
177
- super(name, y_data_points, color)
132
+ # Call the existing data routine for the x/y axis data
133
+ store.add(name, y_data_points, color, x_data_points)
134
+ end
178
135
 
179
- #append the x data to the last entry that was just added in the @data member
180
- last_elem = @data.length() - 1
181
- @data[last_elem] << x_data_points
136
+ alias dataxy data
182
137
 
183
- if @maximum_x_value.nil? && @minimum_x_value.nil?
184
- @maximum_x_value = @minimum_x_value = x_data_points.first
185
- end
138
+ private
139
+
140
+ def setup_data
141
+ # Update the global min/max values for the x data
142
+ @maximum_x_value ||= store.max_x
143
+ @minimum_x_value ||= store.min_x
186
144
 
187
- @maximum_x_value = x_data_points.max > @maximum_x_value ?
188
- x_data_points.max : @maximum_x_value
189
- @minimum_x_value = x_data_points.min < @minimum_x_value ?
190
- x_data_points.min : @minimum_x_value
145
+ super
191
146
  end
192
147
 
193
- protected
148
+ def setup_drawing
149
+ # TODO: Need to get x-axis labels working. Current behavior will be to not allow.
150
+ @labels = {}
151
+
152
+ super
153
+ end
194
154
 
195
155
  def calculate_spread #:nodoc:
196
156
  super
@@ -198,25 +158,10 @@ protected
198
158
  @x_spread = @x_spread > 0 ? @x_spread : 1
199
159
  end
200
160
 
201
- def normalize(force = nil)
202
- if @norm_data.nil? || force
203
- @norm_data = []
204
- return unless @has_data
161
+ def normalize
162
+ return unless data_given?
205
163
 
206
- @data.each do |data_row|
207
- norm_data_points = [data_row[DATA_LABEL_INDEX]]
208
- norm_data_points << data_row[DATA_VALUES_INDEX].map do |r|
209
- (r.to_f - @minimum_value.to_f) / @spread
210
- end
211
- norm_data_points << data_row[DATA_COLOR_INDEX]
212
- norm_data_points << data_row[DATA_VALUES_X_INDEX].map do |r|
213
- (r.to_f - @minimum_x_value.to_f) / @x_spread
214
- end
215
- @norm_data << norm_data_points
216
- end
217
- end
218
- #~ @norm_y_baseline = (@baseline_y_value.to_f / @maximum_value.to_f) if @baseline_y_value
219
- #~ @norm_x_baseline = (@baseline_x_value.to_f / @maximum_x_value.to_f) if @baseline_x_value
164
+ store.normalize(minimum_x: @minimum_x_value, spread_x: @x_spread, minimum_y: minimum_value, spread_y: @spread)
220
165
  end
221
166
 
222
167
  def draw_line_markers
@@ -224,10 +169,8 @@ protected
224
169
  super
225
170
  return if @hide_line_markers
226
171
 
227
- @d = @d.stroke_antialias false
228
-
229
172
  if @x_axis_increment.nil?
230
- # TODO Do the same for larger numbers...100, 75, 50, 25
173
+ # TODO: Do the same for larger numbers...100, 75, 50, 25
231
174
  if @marker_x_count.nil?
232
175
  (3..7).each do |lines|
233
176
  if @x_spread % lines == 0.0
@@ -242,47 +185,36 @@ protected
242
185
  @x_increment = significant(@x_increment)
243
186
  end
244
187
  else
245
- # TODO Make this work for negative values
246
- @maximum_x_value = [@maximum_value.ceil, @x_axis_increment].max
188
+ # TODO: Make this work for negative values
189
+ @maximum_x_value = [maximum_value.ceil, @x_axis_increment].max
247
190
  @minimum_x_value = @minimum_x_value.floor
248
191
  calculate_spread
249
- normalize(true)
192
+ normalize
250
193
 
251
194
  @marker_count = (@x_spread / @x_axis_increment).to_i
252
195
  @x_increment = @x_axis_increment
253
196
  end
254
- @increment_x_scaled = @graph_width.to_f / (@x_spread / @x_increment)
197
+ increment_x_scaled = @graph_width.to_f / (@x_spread / @x_increment)
255
198
 
256
199
  # Draw vertical line markers and annotate with numbers
257
200
  (0..@marker_x_count).each do |index|
258
- # TODO Fix the vertical lines, and enable them by default. Not pretty when they don't match up with top y-axis line
201
+ # TODO: Fix the vertical lines, and enable them by default. Not pretty when they don't match up with top y-axis line
259
202
  if @enable_vertical_line_markers
260
- x = @graph_left + @graph_width - index.to_f * @increment_x_scaled
261
- @d = @d.stroke(@marker_color)
262
- @d = @d.stroke_width 1
263
- @d = @d.line(x, @graph_top, x, @graph_bottom)
203
+ x = @graph_left + @graph_width - index.to_f * increment_x_scaled
204
+ Gruff::Renderer::Line.new(color: @marker_color).render(x, @graph_top, x, @graph_bottom)
264
205
  end
265
206
 
266
207
  unless @hide_line_numbers
267
208
  marker_label = index * @x_increment + @minimum_x_value.to_f
268
209
  y_offset = @graph_bottom + (@x_label_margin || LABEL_MARGIN)
269
- x_offset = get_x_coord(index.to_f, @increment_x_scaled, @graph_left)
270
-
271
- @d.fill = @font_color
272
- @d.font = @font if @font
273
- @d.stroke('transparent')
274
- @d.pointsize = scale_fontsize(@marker_font_size)
275
- @d.gravity = NorthGravity
276
- @d.rotation = -90.0 if @use_vertical_x_labels
277
- @d = @d.annotate_scaled(@base_image,
278
- 1.0, 1.0,
279
- x_offset, y_offset,
280
- vertical_label(marker_label, @x_increment), @scale)
281
- @d.rotation = 90.0 if @use_vertical_x_labels
210
+ x_offset = get_x_coord(index.to_f, increment_x_scaled, @graph_left)
211
+
212
+ label = vertical_label(marker_label, @x_increment)
213
+ rotation = -90.0 if @use_vertical_x_labels
214
+ text_renderer = Gruff::Renderer::Text.new(label, font: @font, size: @marker_font_size, color: @font_color, rotation: rotation)
215
+ text_renderer.render(1.0, 1.0, x_offset, y_offset)
282
216
  end
283
217
  end
284
-
285
- @d = @d.stroke_antialias true
286
218
  end
287
219
 
288
220
  def label(value, increment)
@@ -301,10 +233,7 @@ protected
301
233
  end
302
234
  end
303
235
 
304
- private
305
-
306
236
  def get_x_coord(x_data_point, width, offset) #:nodoc:
307
237
  x_data_point * width + offset
308
238
  end
309
-
310
- end # end Gruff::Scatter
239
+ end
@@ -1,7 +1,8 @@
1
- require "observer"
2
- require File.dirname(__FILE__) + '/base'
1
+ # frozen_string_literal: true
2
+
3
+ require 'observer'
4
+ require 'gruff/base'
3
5
 
4
- ##
5
6
  # A scene is a non-linear graph that assembles layers together to tell a story.
6
7
  # Layers are folders with appropriately named files (see below). You can group
7
8
  # layers and control them together or just set their values individually.
@@ -11,8 +12,6 @@ require File.dirname(__FILE__) + '/base'
11
12
  # * A city scene that changes with the time of day and the weather conditions.
12
13
  # * A traffic map that shows red lines on streets that are crowded and green on free-flowing ones.
13
14
  #
14
- # Usage:
15
- #
16
15
  # g = Gruff::Scene.new("500x100", "path/to/city_scene_directory")
17
16
  #
18
17
  # # Define order of layers, back to front
@@ -30,20 +29,17 @@ require File.dirname(__FILE__) + '/base'
30
29
  # # Write the final graph to disk
31
30
  # g.write "hazy_daytime_city_scene.png"
32
31
  #
33
- #
34
32
  # There are several rules that will magically select a layer when possible.
35
33
  #
36
34
  # * Numbered files will be selected according to the closest value that is less than the input value.
37
- # * 'true.png' and 'false.png' will be used as booleans.
35
+ # * +'true.png'+ and +'false.png'+ will be used as booleans.
38
36
  # * Other named files will be used if the input matches the filename (without the filetype extension).
39
- # * If there is a file named 'default.png', it will be used unless other input values are set for the corresponding layer.
40
-
37
+ # * If there is a file named +'default.png'+, it will be used unless other input values are set for the corresponding layer.
41
38
  class Gruff::Scene < Gruff::Base
42
-
43
- # An array listing the foldernames that will be rendered, from back to front.
44
- #
45
- # g.layers = %w(sky clouds buildings street people)
39
+ # An array listing the folder names that will be rendered, from back to front.
46
40
  #
41
+ # @example
42
+ # g.layers = %w(sky clouds buildings street people)
47
43
  attr_reader :layers
48
44
 
49
45
  def initialize(target_width, base_dir)
@@ -55,9 +51,9 @@ class Gruff::Scene < Gruff::Base
55
51
 
56
52
  def draw
57
53
  # Join all the custom paths and filter out the empty ones
58
- image_paths = @layers.map { |layer| layer.path }.reject { |path| path.empty? }
54
+ image_paths = @layers.map(&:path).reject(&:empty?)
59
55
  images = Magick::ImageList.new(*image_paths)
60
- @base_image = images.flatten_images
56
+ Gruff::Renderer.background_image = images.flatten_images
61
57
  end
62
58
 
63
59
  def layers=(ordered_list)
@@ -77,10 +73,10 @@ class Gruff::Scene < Gruff::Base
77
73
  def method_missing(method_name, *args)
78
74
  case method_name.to_s
79
75
  when /^(\w+)_group=$/
80
- add_group $1, *args
76
+ add_group Regexp.last_match(1), *args
81
77
  return
82
78
  when /^(\w+)=$/
83
- set_input $1, args.first
79
+ set_input Regexp.last_match(1), args.first
84
80
  return
85
81
  end
86
82
  super
@@ -93,19 +89,16 @@ private
93
89
  end
94
90
 
95
91
  def set_input(input_name, input_value)
96
- if not @groups[input_name].nil?
92
+ if !@groups[input_name].nil?
97
93
  @groups[input_name].send_updates(input_value)
98
- else
99
- if chosen_layer = @layers.detect { |layer| layer.name == input_name }
100
- chosen_layer.update input_value
101
- end
94
+ elsif chosen_layer = @layers.find { |layer| layer.name == input_name }
95
+ chosen_layer.update input_value
102
96
  end
103
97
  end
104
-
105
98
  end
106
99
 
100
+ # @private
107
101
  class Gruff::Group
108
-
109
102
  include Observable
110
103
  attr_reader :name
111
104
 
@@ -120,11 +113,10 @@ class Gruff::Group
120
113
  changed
121
114
  notify_observers value
122
115
  end
123
-
124
116
  end
125
117
 
118
+ # @private
126
119
  class Gruff::Layer
127
-
128
120
  attr_reader :name
129
121
 
130
122
  def initialize(base_dir, folder_name)
@@ -149,7 +141,7 @@ class Gruff::Layer
149
141
  when /^-?(\d+\.)?\d+$/
150
142
  select_numeric value
151
143
  when /(\d\d):(\d\d):\d\d/
152
- select_time "#{$1}#{$2}"
144
+ select_time "#{Regexp.last_match(1)}#{Regexp.last_match(2)}"
153
145
  else
154
146
  select_default
155
147
  end
@@ -185,7 +177,8 @@ private
185
177
  return "#{times[index - 1]}.png"
186
178
  end
187
179
  end
188
- return "#{times.last}.png"
180
+
181
+ "#{times.last}.png"
189
182
  end
190
183
 
191
184
  # Match "partly cloudy" to "partly_cloudy.png"
@@ -194,7 +187,7 @@ private
194
187
  end
195
188
 
196
189
  def select_default
197
- @filenames.include?("default.png") ? "default.png" : ''
190
+ @filenames.include?('default.png') ? 'default.png' : ''
198
191
  end
199
192
 
200
193
  # Returns the string "#{filename}.png", if it exists.
@@ -203,5 +196,4 @@ private
203
196
  def file_exists_or_blank(filename)
204
197
  @filenames.include?("#{filename}.png") ? "#{filename}.png" : select_default
205
198
  end
206
-
207
199
  end