technical_graph 0.0.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.
@@ -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