technical_graph 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,374 @@
1
+ h1. How to use it
2
+
3
+ p. All this examples are generated in test/test_technical_readme.rb.
4
+
5
+
6
+
7
+
8
+
9
+ h2. Simple graph
10
+
11
+ p. First thing we need to have data to show on graph, for example something like:
12
+
13
+ <pre>
14
+ <code>
15
+ @simple_data_array = [
16
+ { :x => 0, :y => 0 },
17
+ { :x => 1, :y => 1 },
18
+ { :x => 2, :y => 2 },
19
+ { :x => 3, :y => 2 },
20
+ { :x => 4, :y => 1 },
21
+ { :x => 5, :y => 0 },
22
+ ]
23
+ </code>
24
+ </pre>
25
+
26
+ p. If you want to put this data on graph you need to create TechnicalGraph object
27
+
28
+ <pre>
29
+ <code>
30
+ @tg = TechnicalGraph.new
31
+ </code>
32
+ </pre>
33
+
34
+ p. and add a layer.
35
+
36
+ <pre>
37
+ <code>
38
+ @tg.add_layer(@simple_data_array)
39
+ </code>
40
+ </pre>
41
+
42
+ p. We added data but we don't see anything. Now we have to render graph and save it to file.
43
+
44
+ <pre>
45
+ <code>
46
+ @tg.render
47
+ file_name = 'samples/readme/01_simplest.png'
48
+ @tg.image_drawer.save_to_file(file_name)
49
+ </code>
50
+ </pre>
51
+
52
+ p. And we got our first graph with one layer without any options changes, a bit raw.
53
+
54
+ !https://github.com/akwiatkowski/technical_graph/raw/master/samples/readme/01_simplest.png((01) simple graph)!
55
+
56
+
57
+
58
+
59
+
60
+ h2. More layers
61
+
62
+ p. Maybe example with two layers?
63
+
64
+ <pre>
65
+ <code>
66
+ @simple_data_array = [
67
+ { :x => 0, :y => 0 },
68
+ { :x => 1, :y => 1 },
69
+ { :x => 2, :y => 2 },
70
+ { :x => 3, :y => 2 },
71
+ { :x => 4, :y => 1 },
72
+ { :x => 5, :y => 0 },
73
+ ]
74
+
75
+ @simple_data_array_b = [
76
+ { :x => 0.5, :y => 0.5 },
77
+ { :x => 1.5, :y => 0.5 },
78
+ { :x => 2.5, :y => 1.5 },
79
+ { :x => 3.5, :y => 1.0 },
80
+ { :x => 4.5, :y => 1.5 },
81
+ { :x => 5.5, :y => 1.5 },
82
+ ]
83
+
84
+ @tg = TechnicalGraph.new
85
+ @tg.add_layer(@simple_data_array)
86
+ @tg.add_layer(@simple_data_array_b)
87
+ @tg.render
88
+ file_name = 'samples/readme/02_two_layers.png'
89
+ @tg.image_drawer.save_to_file(file_name)
90
+ </code>
91
+ </pre>
92
+
93
+
94
+ !https://github.com/akwiatkowski/technical_graph/raw/master/samples/readme/02_two_layers.png((02) simple graph)!
95
+
96
+
97
+
98
+
99
+ h2. Ranges
100
+
101
+ p. On default ranges are calculated automatically so all points are visible. You can override ranges setting.
102
+
103
+ <pre>
104
+ <code>
105
+ @simple_data_array = [
106
+ { :x => 0, :y => 0 },
107
+ { :x => 1, :y => 1 },
108
+ { :x => 2, :y => 2 },
109
+ { :x => 3, :y => 2 },
110
+ { :x => 4, :y => 1 },
111
+ { :x => 5, :y => 0 },
112
+ ]
113
+
114
+ @tg = TechnicalGraph.new(
115
+ {
116
+ :x_min => -2,
117
+ :x_max => 10,
118
+ :y_min => -1,
119
+ :y_max => 10,
120
+ })
121
+
122
+ @tg.add_layer(@simple_data_array)
123
+ @tg.render
124
+ file_name = 'samples/readme/03_changed_ranges.png'
125
+ @tg.image_drawer.save_to_file(file_name)
126
+ </code>
127
+ </pre>
128
+
129
+ !https://github.com/akwiatkowski/technical_graph/raw/master/samples/readme/03_changed_ranges.png((03) changed ranges)!
130
+
131
+
132
+ p. Keep in mind that ranges will be changed but any point of graph can enlarge ranges that all points will be visible, unless...
133
+
134
+
135
+
136
+
137
+
138
+ h2. Fixed ranges
139
+
140
+ p. You can turn off automatic range enlargement algorithm by using:
141
+
142
+ <pre>
143
+ <code>
144
+ options[:xy_behaviour] = :fixed
145
+ </code>
146
+ </pre>
147
+
148
+ Example:
149
+
150
+ <pre>
151
+ <code>
152
+ @simple_data_array = [
153
+ { :x => 0, :y => 0 },
154
+ { :x => 1, :y => 1 },
155
+ { :x => 2, :y => 2 },
156
+ { :x => 3, :y => 2 },
157
+ { :x => 4, :y => 1 },
158
+ { :x => 5, :y => 0 },
159
+ ]
160
+
161
+ @tg = TechnicalGraph.new(
162
+ {
163
+ :x_min => 1,
164
+ :x_max => 2,
165
+ :y_min => 1,
166
+ :y_max => 2,
167
+ :xy_behaviour => :fixed,
168
+ })
169
+
170
+ @tg.add_layer(@simple_data_array)
171
+ @tg.render
172
+ file_name = 'samples/readme/04_changed_ranges_fixed.png'
173
+ @tg.image_drawer.save_to_file(file_name)
174
+ </code>
175
+ </pre>
176
+
177
+ !https://github.com/akwiatkowski/technical_graph/raw/master/samples/readme/04_changed_ranges_fixed.png((04) changed ranges)!
178
+
179
+
180
+
181
+
182
+ h2. Axis interval - fixed count
183
+
184
+ p. Axis can be calculated using two algorithms:
185
+
186
+ # fixed interval - axis is every some distance,
187
+ # fixed count - there is fixed amount of axis on graph.
188
+
189
+ p. Keep in mind that where is X (parameter) and Y (value) axis. If you want to set fixed amount you should set
190
+ options[:x_axis_fixed_interval] and/or options[:y_axis_fixed_interval] to false, like in the example below.
191
+
192
+ <pre>
193
+ <code>
194
+ @simple_data_array = [
195
+ { :x => 0, :y => 0 },
196
+ { :x => 1, :y => 1 },
197
+ { :x => 2, :y => 2 },
198
+ { :x => 3, :y => 2 },
199
+ { :x => 4, :y => 1 },
200
+ { :x => 5, :y => 0 },
201
+ ]
202
+
203
+ @tg = TechnicalGraph.new(
204
+ {
205
+ :x_axis_fixed_interval => false,
206
+ :y_axis_fixed_interval => false,
207
+ :y_axis_count => 20,
208
+ :x_axis_count => 20,
209
+ })
210
+
211
+ @tg.add_layer(@simple_data_array)
212
+ @tg.render
213
+ file_name = 'samples/readme/05_axis_fixed_amount.png'
214
+ @tg.image_drawer.save_to_file(file_name)
215
+ </code>
216
+ </pre>
217
+
218
+ !https://github.com/akwiatkowski/technical_graph/raw/master/samples/readme/05_axis_fixed_amount.png((05) fixed axis count)!
219
+
220
+
221
+
222
+
223
+ h2. Axis interval - fixed interval
224
+
225
+ p. Axis can be calculated using two algorithms:
226
+
227
+ # fixed interval - axis is every some distance,
228
+ # fixed count - there is fixed amount of axis on graph.
229
+
230
+ p. Keep in mind that where is X (parameter) and Y (value) axis. If you want to set fixed amount you should set
231
+ options[:x_axis_fixed_interval] and/or options[:y_axis_fixed_interval] to false, like in the example below.
232
+
233
+ <pre>
234
+ <code>
235
+ @simple_data_array = [
236
+ { :x => 0, :y => 0 },
237
+ { :x => 1, :y => 1 },
238
+ { :x => 2, :y => 2 },
239
+ { :x => 3, :y => 2 },
240
+ { :x => 4, :y => 1 },
241
+ { :x => 5, :y => 0 },
242
+ ]
243
+
244
+ @tg = TechnicalGraph.new(
245
+ {
246
+ :x_axis_fixed_interval => true,
247
+ :y_axis_fixed_interval => true,
248
+ :y_axis_interval => 0.8,
249
+ :x_axis_interval => 0.6,
250
+ })
251
+ @tg.add_layer(@simple_data_array)
252
+ @tg.render
253
+ file_name = 'samples/readme/06_axis_fixed_interval.png'
254
+ @tg.image_drawer.save_to_file(file_name)
255
+ </code>
256
+ </pre>
257
+
258
+ !https://github.com/akwiatkowski/technical_graph/raw/master/samples/readme/06_axis_fixed_interval.png((06) fixed axis interval)!
259
+
260
+
261
+
262
+
263
+
264
+
265
+ h2. Axis labels
266
+
267
+ p. You can add label to X and Y axis. using options[:x_axis_label] and options[:y_axis_label], and you can choose font
268
+ size using options[:axis_label_font_size].
269
+
270
+ <pre>
271
+ <code>
272
+ @simple_data_array = [
273
+ { :x => 0, :y => 0 },
274
+ { :x => 1, :y => 1 },
275
+ { :x => 2, :y => 2 },
276
+ { :x => 3, :y => 2 },
277
+ { :x => 4, :y => 1 },
278
+ { :x => 5, :y => 0 },
279
+ ]
280
+
281
+ @tg = TechnicalGraph.new(
282
+ {
283
+ :x_axis_label => 'parameter',
284
+ :y_axis_label => 'value',
285
+ :axis_label_font_size => 36
286
+ })
287
+ @@tg.add_layer(@simple_data_array)
288
+ @tg.render
289
+ file_name = 'samples/readme/07_axis_label.png'
290
+ @tg.image_drawer.save_to_file(file_name)
291
+ </code>
292
+ </pre>
293
+
294
+ !https://github.com/akwiatkowski/technical_graph/raw/master/samples/readme/07_axis_label.png((07) axis labels)!
295
+
296
+
297
+
298
+ h2. Float numbers and value labels
299
+
300
+ p. On default all float number are shown using 2 digits after dot. You can override it using options[:truncate_string]
301
+ like in example below. If you want have values near graph points you can use proper layer options.
302
+
303
+ p. Layers have also options, and it is useful for this example to turn on value labels.
304
+
305
+ layer_options[:value_labels]
306
+
307
+ <pre>
308
+ <code>
309
+ @float_data_array = [
310
+ { :x => 0, :y => 0 },
311
+ { :x => 0.111, :y => 0.123 },
312
+ { :x => 0.222, :y => 1.456 },
313
+ { :x => 0.333, :y => 2.8756 },
314
+ { :x => 0.555, :y => 1.042 },
315
+ { :x => 0.888, :y => 0.988 },
316
+ ]
317
+
318
+ @tg = TechnicalGraph.new(
319
+ {
320
+ :truncate_string => "%.3f"
321
+ })
322
+ @layer_params = {
323
+ :value_labels => true
324
+ }
325
+ @tg.add_layer(@float_data_array, @layer_params)
326
+ @tg.render
327
+ file_name = 'samples/readme/08a_truncate_string.png'
328
+ @tg.image_drawer.save_to_file(file_name)
329
+ </code>
330
+ </pre>
331
+
332
+ !https://github.com/akwiatkowski/technical_graph/raw/master/samples/readme/08a_truncate_string.png((08a) displaying float numbers)!
333
+
334
+ <pre>
335
+ <code>
336
+ @float_data_array = [
337
+ { :x => 0, :y => 0 },
338
+ { :x => 0.111, :y => 0.123 },
339
+ { :x => 0.222, :y => 1.456 },
340
+ { :x => 0.333, :y => 2.8756 },
341
+ { :x => 0.555, :y => 1.042 },
342
+ { :x => 0.888, :y => 0.988 },
343
+ ]
344
+
345
+ @tg = TechnicalGraph.new(
346
+ {
347
+ :truncate_string => "%.1f"
348
+ })
349
+ @layer_params = {
350
+ :value_labels => true
351
+ }
352
+ @tg.add_layer(@float_data_array, @layer_params)
353
+ @tg.render
354
+ file_name = 'samples/readme/08b_truncate_string.png'
355
+ @tg.image_drawer.save_to_file(file_name)
356
+ </code>
357
+ </pre>
358
+
359
+ !https://github.com/akwiatkowski/technical_graph/raw/master/samples/readme/08b_truncate_string.png((08b) displaying float numbers)!
360
+
361
+
362
+ h2. TODO
363
+
364
+ # Graph image size
365
+ # Graph colors: background, hatch (option to turn it off?), axis
366
+ # Anti-aliasing: image size comparison, layer antialiases
367
+ # Font sizes
368
+ # Axis density checking algorithm: sample when it us useful
369
+ # Layer labels: used in legend
370
+ # Layer colors, random colors
371
+ # Legend with set position
372
+ # Legend with auto position
373
+ # Smoother:
374
+ # Noise removal
data/README.md CHANGED
@@ -1,11 +1,38 @@
1
- technical-graph
1
+ technical_graph
2
2
  ===============
3
3
 
4
- Create neat graphs.
4
+ Purpose of this gem is to create neat, meaningful, linear graphs for large amount of data.
5
5
 
6
+ If you want to:
6
7
 
7
- Usage
8
- -----
8
+ * create big graphs using large amount of data,
9
+ * do it offline,
10
+ * minimize needed code,
11
+ * use only linear graph,
12
+ * speed is not essential,
13
+ * RMagick / ImageMagick is ok for you,
14
+ * tired of forgotten gems/libraries...
15
+
16
+ then you should try this gem.
17
+
18
+ I created it because there were not available and maintained gems for that I needed. Now I use it to create hourly
19
+ temperature and wind graphs for vast period of time (months, years), visualize measurements for [HomeIO](https://github.com/akwiatkowski/HomeIO).
20
+
21
+ If you want to create candy, ultra fast, web graphs it maybe not the best tool. If you want other graph types than linear it
22
+ is definitely not the right tool for you. It is also not SVG ready yet, but it should be within a few months.
23
+ You can find my competitors [here](https://www.ruby-toolbox.com/categories/graphing).
24
+
25
+
26
+ Future
27
+ ------
28
+
29
+ 1. Finish data processors: smoothing (nearly done), noise removal (50%), interpolation or curved graphs (planned).
30
+ 2. Fix export to SVG.
31
+ 3. Optimization, and if needed find or write something faster for creating SVGs.
32
+
33
+
34
+ Quick start
35
+ -----------
9
36
 
10
37
  Check currents test when documentation is not enough :)
11
38
 
@@ -32,7 +59,7 @@ or
32
59
  where:
33
60
 
34
61
  * layer_data - Array of Hashes, like [{:x => 0, :y => 0}, {:x => 1, :y => 1}, ...]
35
- * layer_params - Hash of other parameters, all parameters will be described later.
62
+ * layer_params - Hash of other parameters, all parameters are described later.
36
63
 
37
64
  3. Render graph
38
65
 
@@ -46,101 +73,13 @@ or get image binary content.
46
73
 
47
74
  > tg.image_drawer.to_format(format)
48
75
 
49
- where format is image format, ex. 'png', 'jpeg', ...
76
+ where format is image format, ex. 'png', 'jpeg', ... Of course I prefer 'png'.
50
77
 
51
78
 
52
- Options Hash
79
+ Documentation
53
80
  -------------
54
81
 
55
- Default ranges:
56
-
57
- * options[:x_min]
58
- * options[:x_max]
59
- * options[:y_min]
60
- * options[:y_max]
61
-
62
- Ranges calculation mode:
63
-
64
- * options[:xy_behaviour] = :default - ranges can be enlarged
65
- * options[:xy_behaviour] = :fixed - fixed ranges
66
-
67
- Axis can be calculated using fixed interval or fixed count per graph.
68
-
69
- * options[:x_axis_fixed_interval] = true
70
- * options[:y_axis_fixed_interval] = true
71
-
72
- If fixed interval is set you should specify interval:
73
-
74
- * options[:y_axis_interval] = 1.0
75
- * options[:x_axis_interval] = 1.0
76
-
77
- ...else, count of axis:
78
-
79
- * options[:y_axis_count] = 10
80
- * options[:x_axis_count] = 10
81
-
82
- Axis labels:
83
-
84
- * options[:x_axis_label] = 'x'
85
- * options[:y_axis_label] = 'y'
86
-
87
-
88
- Labels has truncate string to define precision. Default it is "%.2f".
89
-
90
- * options[:truncate_string] = "%.2f"
91
-
92
- Graph image size:
93
-
94
- * options[:width]
95
- * options[:height]
96
-
97
- Colors:
98
-
99
- Possible #RRGGBB or color names (ex. 'white').
100
-
101
- * options[:background_color] - background color of image
102
- * options[:background_hatch_color] - background hatch color
103
- * options[:axis_color] - color of axis
104
-
105
- Anti-aliasing:
106
-
107
- * options[:axis_antialias] - use anti-aliasing for axis, default false
108
- * options[:layers_antialias] - use anti-aliasing for data layers, default false, can be override using layer option
109
- * options[:font_antialias] - use anti-aliasing for all fonts, default false
110
-
111
- Font size:
112
-
113
- * options[:layers_font_size] - size of font used for values in graph
114
- * options[:axis_font_size] - size of font used in axis
115
- * options[:axis_label_font_size] - size of font used in options[:x_axis_label] and options[:y_axis_label]
116
-
117
- Sometime because of axis options and large amount of data, axis can be put densely on graph. Turning this option graph
118
- size will be enlarged to maintain set distanced between axis.
119
-
120
- * options[:axis_density_enlarge_image] - turn this options on
121
- * options[:x_axis_min_distance] - minimum distance between X axis, default 30 pixels
122
- * options[:y_axis_min_distance] - minimum distance between X axis, default 50 pixels
123
-
124
- Legend options:
125
-
126
- * options[:legend] - do you want to draw legend?, default false
127
- * options[:legend_auto] - let legend position to be chosen by algorithm, default true
128
- * options[:legend_width] - width used for setting proper distance while drawing on right, default 100, legend height is calculated
129
- * options[:legend_margin] - graph margin used not to draw legend on border, default 50
130
- * options[:legend_x] - legend X position, used when options[:legend_auto] is false, default 50
131
- * options[:legend_y] - legend Y position, used when options[:legend_auto] is false, default 50
132
-
133
-
134
- Layer options Hash
135
- ------------------
136
-
137
- * layer_options[:label] - label used in legend
138
- * layer_options[:color] - color of graph layer, ex.: 'red', 'green', '#FFFF00'
139
- * layer_options[:antialias] - use anti-aliasing for this, default false, override options[:layers_antialias]
140
- * layer_options[:value_labels] - write values near 'dots', default true
141
- * layer_options[:simple_smoother] - 'smooth' data, default false
142
- * layer_options[:simple_smoother_level] - strength of smoothing, this is level of window used for processing, default 3
143
- * layer_options[:simple_smoother_strategy] - strategy used for smoothing data, you can choose between :rectangular or :gauss, default :rectangular
82
+ Documentation is moved [here](https://github.com/akwiatkowski/technical_graph/blob/master/DOCUMENTATION.textile)
144
83
 
145
84
 
146
85
  Contributing to technical-graph
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.0
1
+ 0.3.1
@@ -0,0 +1,14 @@
1
+ #encoding: utf-8
2
+
3
+ # Additional methods for array
4
+ # http://stackoverflow.com/questions/1341271/average-from-a-ruby-array
5
+
6
+ class Array
7
+ def sum
8
+ inject(0.0) { |result, el| result + el }
9
+ end
10
+
11
+ def mean
12
+ sum / size
13
+ end
14
+ end
@@ -1,5 +1,6 @@
1
1
  #encoding: utf-8
2
2
 
3
+ require 'technical_graph/data_point'
3
4
  require 'technical_graph/graph_color_library'
4
5
  require 'technical_graph/data_layer_processor'
5
6
 
@@ -20,8 +21,9 @@ class DataLayer
20
21
  # smoothing parameters
21
22
  # by default it is false
22
23
  @data_params[:simple_smoother] = true if options[:simple_smoother] == true
24
+ @data_params[:simple_smoother_x] = true if options[:simple_smoother_x] == true
23
25
  @data_params[:simple_smoother_level] ||= 3
24
- @data_params[:simple_smoother_strategy] ||= DataLayerProcessor::DEFAULT_STRATEGY
26
+ @data_params[:simple_smoother_strategy] ||= DataLayerProcessor::DEFAULT_SIMPLE_SMOOTHER_STRATEGY
25
27
  # was already done
26
28
  @data_params[:processor_finished] = false
27
29
 
@@ -36,29 +38,42 @@ class DataLayer
36
38
  attr_reader :processor
37
39
 
38
40
  # Accessor for setting chart data for layer to draw
39
- def append_data(d)
40
- if d.kind_of? Array
41
- @data += d
41
+ def append_data(data_array)
42
+ if data_array.kind_of? Array
43
+ # append as DataPoint
44
+ # convert to DataPoints, which has more specialized methods
45
+ data_array.each do |d|
46
+ @data << DataPoint.new(d)
47
+ end
48
+
42
49
  # sort, clean bad records
43
- process_data
50
+ process_data_internal
51
+
52
+ # @raw_data is dirty, deleting @processed_data
53
+ @processed_data = nil
44
54
  else
45
55
  raise 'Data not an Array'
46
56
  end
47
57
  end
48
58
 
49
- # Array of {:x =>, :y =>}
50
- attr_reader :data
59
+ # Array of DataPoints, not processed
60
+ def raw_data
61
+ @data
62
+ end
51
63
 
52
- # Run external processor (smoothing, ...)
53
- def process
54
- if simple_smoother and not processor_finished?
55
- @processor.strategy = simple_smoother_strategy
56
- @processor.level = simple_smoother_level
57
- @processor.generate_vector
58
- @data = @processor.process
59
-
60
- processor_finished!
64
+ # Array of DataPoints, after external processing
65
+ def processed_data
66
+ if @processed_data.nil?
67
+ process!
61
68
  end
69
+
70
+ @processed_data
71
+ end
72
+
73
+ # Run external processor (smoothing, ...)
74
+ def process!
75
+ @processed_data = @data.clone
76
+ @processed_data = @processor.process
62
77
  end
63
78
 
64
79
  # Additional parameters
@@ -97,14 +112,8 @@ class DataLayer
97
112
  return @data_params[:simple_smoother_strategy]
98
113
  end
99
114
 
100
- # Was already processed?
101
- def processor_finished?
102
- @data_params[:processor_finished]
103
- end
104
-
105
- # Mark as processed
106
- def processor_finished!
107
- @data_params[:processor_finished] = true
115
+ def simple_smoother_x
116
+ return @data_params[:simple_smoother_x]
108
117
  end
109
118
 
110
119
  # Clear data
@@ -113,24 +122,23 @@ class DataLayer
113
122
  end
114
123
 
115
124
  # Clean and process data used for drawing current data layer
116
- def process_data
125
+ def process_data_internal
117
126
  # delete duplicates
118
- @data = @data.inject([]) { |result, d| result << d unless result.select { |r| r[:x] == d[:x] }.size > 0; result }
127
+ @data = @data.inject([]) { |result, d| result << d unless result.select { |r| r.x == d.x }.size > 0; result }
119
128
 
120
- @data.delete_if { |d| d[:x].nil? or d[:y].nil? }
121
- @data.sort! { |d, e| d[:x] <=> e[:x] }
129
+ @data.delete_if { |d| d.x.nil? or d.y.nil? }
130
+ @data.sort! { |d, e| d.x <=> e.x }
122
131
 
123
132
  # default X values, if data is not empty
124
133
  if @data.size > 0
125
- @data_params[:x_min] = @data.first[:x] || @options[:default_x_min]
126
- @data_params[:x_max] = @data.last[:x] || @options[:default_x_max]
134
+ @data_params[:x_min] = @data.first.x || @options[:default_x_min]
135
+ @data_params[:x_max] = @data.last.x || @options[:default_x_max]
127
136
 
128
137
  # default Y values
129
- y_sort = @data.sort { |a, b| a[:y] <=> b[:y] }
130
- @data_params[:y_min] = y_sort.first[:y] || @options[:default_y_min]
131
- @data_params[:y_max] = y_sort.last[:y] || @options[:@default_y_max]
138
+ y_sort = @data.sort { |a, b| a.y <=> b.y }
139
+ @data_params[:y_min] = y_sort.first.y || @options[:default_y_min]
140
+ @data_params[:y_max] = y_sort.last.y || @options[:@default_y_max]
132
141
  end
133
-
134
142
  end
135
143
 
136
144
  def x_min