technical_graph 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,306 @@
1
+ require 'helper'
2
+
3
+ class TestTechnicalGraphAxis < Test::Unit::TestCase
4
+ context 'ranges calculation' do
5
+ setup do
6
+ end
7
+
8
+ should 'provide defined ranges' do
9
+ x_min = 0.0
10
+ x_max = 10.0
11
+ y_min = -5.0
12
+ y_max = 5.0
13
+
14
+ @tg = TechnicalGraph.new(
15
+ {
16
+ :x_min => x_min,
17
+ :x_max => x_max,
18
+ :y_min => y_min,
19
+ :y_max => y_max,
20
+ :xy_behaviour => :fixed
21
+ }
22
+ )
23
+ @tg.render
24
+
25
+ @tg.axis.x_min.should == x_min
26
+ @tg.axis.x_max.should == x_max
27
+ @tg.axis.y_min.should == y_min
28
+ @tg.axis.y_max.should == y_max
29
+ end
30
+
31
+ should 'calculate ranges per layer' do
32
+ # adding simple layer
33
+ layer_data = [
34
+ { :x => -1, :y => 3.0 },
35
+ { :x => 3, :y => -8.0 },
36
+ { :x => 0, :y => 3.0 },
37
+ { :x => 10, :y => 10.0 }
38
+ ]
39
+ dl = DataLayer.new(layer_data)
40
+
41
+ dl.x_min.should == -1
42
+ dl.x_max.should == 10
43
+ dl.y_min.should == -8.0
44
+ dl.y_max.should == 10.0
45
+ end
46
+
47
+
48
+ should 'provide ranges calculated using data layer, and multiple layers' do
49
+ x_min = 0.0
50
+ x_max = 1.0
51
+ y_min = -1.0
52
+ y_max = 1.0
53
+
54
+ @tg = TechnicalGraph.new(
55
+ {
56
+ :x_min => x_min,
57
+ :x_max => x_max,
58
+ :y_min => y_min,
59
+ :y_max => y_max,
60
+ :xy_behaviour => :default
61
+ }
62
+ )
63
+
64
+ # adding simple layer
65
+ layer_data = [
66
+ { :x => -1, :y => 3.0 },
67
+ { :x => 3, :y => -8.0 },
68
+ { :x => 0, :y => 3.0 },
69
+ { :x => 10, :y => 10.0 }
70
+ ]
71
+ @tg.add_layer(layer_data)
72
+ # should be added
73
+ @tg.layers.last.data.size > 0
74
+ # checking ranger for layer
75
+
76
+ @tg.render
77
+
78
+
79
+ @tg.axis.x_min.should_not == x_min
80
+ @tg.axis.x_max.should_not == x_max
81
+ @tg.axis.y_min.should_not == y_min
82
+ @tg.axis.y_max.should_not == y_max
83
+
84
+ @tg.axis.x_min.should == -1
85
+ @tg.axis.x_max.should == 10
86
+ @tg.axis.y_min.should == -8.0
87
+ @tg.axis.y_max.should == 10.0
88
+
89
+
90
+
91
+ # adding another layer
92
+
93
+ # adding simple layer
94
+ layer_data = [
95
+ { :x => -21, :y => -93.0 },
96
+ { :x => -5, :y => 3.0 },
97
+ { :x => 39, :y => -8.0 },
98
+ { :x => 0, :y => 333.0 },
99
+ { :x => 10, :y => 50.0 }
100
+ ]
101
+ @tg.add_layer(layer_data)
102
+ # should be added
103
+ @tg.layers.last.data.size > 1
104
+
105
+ @tg.render
106
+
107
+ @tg.axis.x_min.should_not == x_min
108
+ @tg.axis.x_max.should_not == x_max
109
+ @tg.axis.y_min.should_not == y_min
110
+ @tg.axis.y_max.should_not == y_max
111
+
112
+ @tg.axis.x_min.should_not == -1.0
113
+ @tg.axis.x_max.should_not == 10.0
114
+ @tg.axis.y_min.should_not == -8.0
115
+ @tg.axis.y_max.should_not == 10.0
116
+
117
+ @tg.axis.x_min.should == -21.0
118
+ @tg.axis.x_max.should == 39.0
119
+ @tg.axis.y_min.should == -93.0
120
+ @tg.axis.y_max.should == 333.0
121
+ end
122
+
123
+ should 'provide ranges calculated with zoom' do
124
+ x_min = 0.0
125
+ x_max = 1.0
126
+ y_min = -1.0
127
+ y_max = 1.0
128
+
129
+ @tg = TechnicalGraph.new(
130
+ {
131
+ :x_min => x_min,
132
+ :x_max => x_max,
133
+ :y_min => y_min,
134
+ :y_max => y_max,
135
+ :xy_behaviour => :default
136
+ }
137
+ )
138
+
139
+ # adding simple layer
140
+ layer_data = [
141
+ { :x => -5, :y => 5.0 },
142
+ { :x => 2, :y => -5.0 },
143
+ { :x => 0, :y => 5.0 },
144
+ { :x => 5, :y => 5.0 }
145
+ ]
146
+ @tg.add_layer(layer_data)
147
+ # should be added
148
+ @tg.layers.last.data.size > 0
149
+ # checking ranger for layer
150
+
151
+ @tg.render
152
+
153
+
154
+ @tg.axis.x_min.should_not == x_min
155
+ @tg.axis.x_max.should_not == x_max
156
+ @tg.axis.y_min.should_not == y_min
157
+ @tg.axis.y_max.should_not == y_max
158
+
159
+ @tg.axis.x_min.should == -5
160
+ @tg.axis.x_max.should == 5
161
+ @tg.axis.y_min.should == -5.0
162
+ @tg.axis.y_max.should == 5.0
163
+
164
+ @tg.axis.zoom = 2.0
165
+
166
+ @tg.axis.x_min.should == -10.0
167
+ @tg.axis.x_max.should == 10.0
168
+ @tg.axis.y_min.should == -10.0
169
+ @tg.axis.y_max.should == 10.0
170
+
171
+ @tg.axis.x_min.should_not == -5
172
+ @tg.axis.x_max.should_not == 5
173
+ @tg.axis.y_min.should_not == -5.0
174
+ @tg.axis.y_max.should_not == 5.0
175
+
176
+ @tg.axis.raw_x_min.should == -5
177
+ @tg.axis.raw_x_max.should == 5
178
+ @tg.axis.raw_y_min.should == -5.0
179
+ @tg.axis.raw_y_max.should == 5.0
180
+ end
181
+
182
+
183
+ should 'calculate axis with fixed interval' do
184
+ x_min = -5.0
185
+ x_max = 5.0
186
+ y_min = -5.0
187
+ y_max = 5.0
188
+
189
+ @tg = TechnicalGraph.new(
190
+ {
191
+ :x_min => x_min,
192
+ :x_max => x_max,
193
+ :y_min => y_min,
194
+ :y_max => y_max,
195
+ :xy_behaviour => :fixed,
196
+
197
+ :y_axises_count => 10,
198
+ :x_axises_count => 10,
199
+ :y_axises_interval => 1.0,
200
+ :x_axises_interval => 4.0,
201
+ :x_axises_fixed_interval => true,
202
+ :y_axises_fixed_interval => true
203
+ }
204
+ )
205
+
206
+ # adding simple layer
207
+ layer_data = [
208
+ { :x => -1, :y => 2.0 },
209
+ { :x => 1, :y => -2.0 },
210
+ { :x => 0, :y => 2.0 },
211
+ { :x => 1, :y => 2.0 }
212
+ ]
213
+ @tg.add_layer(layer_data)
214
+ # should be added
215
+ @tg.layers.last.data.size > 0
216
+ # checking ranger for layer
217
+
218
+ @tg.render
219
+
220
+ @tg.axis.value_axises.should == [-5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0]
221
+ @tg.axis.parameter_axises.should == [-5.0, -1.0, 3.0]
222
+ end
223
+
224
+
225
+ should 'calculate axis with fixed count' do
226
+ x_min = -8.0
227
+ x_max = 8.0
228
+ y_min = -4.0
229
+ y_max = 4.0
230
+
231
+ @tg = TechnicalGraph.new(
232
+ {
233
+ :x_min => x_min,
234
+ :x_max => x_max,
235
+ :y_min => y_min,
236
+ :y_max => y_max,
237
+ :xy_behaviour => :fixed,
238
+
239
+ :x_axises_count => 8,
240
+ :y_axises_count => 4,
241
+ :x_axises_interval => 2.0,
242
+ :y_axises_interval => 1.0,
243
+ :x_axises_fixed_interval => false,
244
+ :y_axises_fixed_interval => false
245
+ }
246
+ )
247
+
248
+ # adding simple layer
249
+ layer_data = [
250
+ { :x => -1, :y => 2.0 },
251
+ { :x => 1, :y => -2.0 },
252
+ { :x => 0, :y => 2.0 },
253
+ { :x => 1, :y => 2.0 }
254
+ ]
255
+ @tg.add_layer(layer_data)
256
+ # should be added
257
+ @tg.layers.last.data.size > 0
258
+ # checking ranger for layer
259
+
260
+ @tg.render
261
+
262
+ @tg.axis.parameter_axises.should == [-8.0, -6.0, -4.0, -2.0, 0.0, 2.0, 4.0, 6.0]
263
+ @tg.axis.value_axises.should == [-4.0, -2.0, 0.0, 2.0]
264
+
265
+ @tg.image.save_to_file('test1.png')
266
+ end
267
+
268
+
269
+ should 'draw simple graph' do
270
+ @tg = TechnicalGraph.new(
271
+ {
272
+ :x_axises_count => 10,
273
+ :y_axises_count => 10,
274
+ :x_axises_interval => 1.0,
275
+ :y_axises_interval => 1.0,
276
+ :x_axises_fixed_interval => false,
277
+ :y_axises_fixed_interval => false,
278
+
279
+ :x_min => -10.0,
280
+ :x_max => 10.0,
281
+ :y_min => -10.0,
282
+ :y_max => 10.0
283
+ }
284
+ )
285
+
286
+
287
+
288
+ # adding simple layer
289
+ layer_data = Array.new
290
+ (0..20).each do |i|
291
+ layer_data << {:x => Math.sin(i.to_f/10.0) * 10.0, :y => Math.cos(i.to_f/10.0) * 10.0 }
292
+ end
293
+ @tg.add_layer(layer_data)
294
+ # should be added
295
+ @tg.layers.last.data.size > 0
296
+ # checking ranger for layer
297
+
298
+ @tg.render
299
+
300
+ @tg.image.save_to_file('test2.png')
301
+ end
302
+
303
+ end
304
+
305
+ end
306
+
@@ -0,0 +1,55 @@
1
+ #encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'technical_graph/data_layer'
5
+ require 'technical_graph/graph_data_processor'
6
+ require 'technical_graph/graph_image_drawer'
7
+ require 'technical_graph/graph_axis'
8
+
9
+ # Universal class for creating graphs/charts.
10
+
11
+ # options parameters:
12
+ # :width - width of image
13
+ # :height - height of image
14
+ # :x_min, :x_max, :y_min, :y_max - default or fixed ranges
15
+ # :xy_behaviour:
16
+ # * :default - use them as default ranges
17
+ # * :fixed - ranges will not be changed during addition of layers
18
+
19
+ class TechnicalGraph
20
+
21
+ def initialize(options = { })
22
+ @options = options
23
+ @data_processor = GraphDataProcessor.new(self)
24
+ @image_drawer = GraphImageDrawer.new(self)
25
+ @axis = GraphAxis.new(self)
26
+ @layers = Array.new
27
+ end
28
+ attr_reader :options
29
+ attr_reader :data_processor
30
+ attr_reader :image_drawer
31
+ attr_reader :axis
32
+
33
+ attr_reader :layers
34
+
35
+ # Add new data layer to layer array
36
+ def add_layer(data = [], options = {})
37
+ @layers << DataLayer.new(data, options)
38
+ end
39
+
40
+ # Create graph
41
+ def render
42
+ @image = @image_drawer.crate_blank_graph_image
43
+ # recalculate ranges if needed
44
+ @layers.each do |l|
45
+ @data_processor.process_data_layer(l)
46
+ end
47
+
48
+ # draw axis
49
+ @axis.render_on_image(@image)
50
+ # draw layers
51
+ @layers.each do |l|
52
+ @image_drawer.render_data_layer(l)
53
+ end
54
+ end
55
+ end
data/samples/1.png ADDED
Binary file
data/test/helper.rb ADDED
@@ -0,0 +1,19 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'test/unit'
11
+ require 'shoulda'
12
+ require 'rspec'
13
+
14
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
15
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
16
+ require 'technical_graph'
17
+
18
+ class Test::Unit::TestCase
19
+ end
@@ -0,0 +1,104 @@
1
+ require 'helper'
2
+
3
+ class TestTechnicalGraph < Test::Unit::TestCase
4
+ context 'initial options' do
5
+ setup do
6
+ @technical_graph = TechnicalGraph.new
7
+ end
8
+
9
+ should "has options with default values" do
10
+ @technical_graph.options.class.should == Hash
11
+ @technical_graph.options[:width] > 0
12
+ @technical_graph.options[:height] > 0
13
+ end
14
+
15
+ should "has options with custom values" do
16
+ s = 10
17
+ tg = TechnicalGraph.new({ :height => s, :width => s })
18
+ @technical_graph.options[:width] == s
19
+ @technical_graph.options[:height] == s
20
+
21
+ @technical_graph.image_drawer.width == s
22
+ @technical_graph.image_drawer.height == s
23
+ end
24
+
25
+ should "has changeable options" do
26
+ s = 20
27
+ tg = TechnicalGraph.new
28
+ tg.image_drawer.width = s
29
+ tg.image_drawer.height = s
30
+ @technical_graph.options[:width] == s
31
+ @technical_graph.options[:height] == s
32
+ end
33
+ end
34
+
35
+ context 'basic layer operation and saving file' do
36
+ setup do
37
+ @tg = TechnicalGraph.new
38
+ @data_size = 100
39
+
40
+ # sample data
41
+ @data = Array.new
42
+ @second_data = Array.new
43
+ (0...@data_size).each do |i|
44
+ @data << { :x => Time.now.to_i - 3600 + i, :y => Math.sin(i.to_f / 10.0) }
45
+ @second_data << { :x => Time.now.to_i - 1800 + i*2, :y => Math.cos(i.to_f / 10.0) }
46
+ end
47
+ end
48
+
49
+ should 'has ability do add new layer' do
50
+ layers = @tg.layers.size
51
+ @tg.add_layer(@data)
52
+ @tg.layers.size.should == layers + 1
53
+
54
+ layer = @tg.layers.last
55
+ layer.data.size.should == @data_size
56
+ end
57
+
58
+ should 'has ability to manipulate layers, add more data' do
59
+ @tg.add_layer(@data)
60
+ layer = @tg.layers.last
61
+ layer.class.should == DataLayer
62
+
63
+ layer.data.size.should == @data_size
64
+
65
+ # adding second data
66
+ layer.append_data(@second_data)
67
+ layer.data.size.should == 2 * @data_size
68
+
69
+ # @tg.render
70
+ # @tg.image.save_to_file('test1.png')
71
+ end
72
+
73
+ should 'has ability to filter records with similar x\'es' do
74
+ @tg.add_layer
75
+ layer = @tg.layers.last
76
+ layer.data.size.should == 0
77
+ layer.append_data([{ :x => 0, :y => 1 }])
78
+ layer.data.size.should == 1
79
+
80
+ # uniq check
81
+ layer.append_data([{ :x => 0, :y => 1 }])
82
+ layer.append_data([{ :x => 0, :y => 1 }])
83
+ layer.data.size.should == 1
84
+ layer.append_data([{ :x => 2, :y => 1 }])
85
+ layer.data.size.should == 2
86
+ end
87
+
88
+ should 'has ability to filter bad records' do
89
+ @tg.add_layer
90
+ layer = @tg.layers.last
91
+ layer.data.size.should == 0
92
+ layer.append_data([{ :x => 0, :y => 1 }])
93
+ layer.data.size.should == 1
94
+
95
+ # uniq check
96
+ layer.append_data([{ :z => 0, :y => 1 }])
97
+ layer.append_data([{}])
98
+ layer.data.size.should == 1
99
+ end
100
+
101
+ end
102
+
103
+
104
+ end