technical_graph 0.2.0 → 0.3.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.
- data/README.md +4 -0
- data/Rakefile +2 -2
- data/VERSION +1 -1
- data/lib/technical_graph.rb +3 -0
- data/lib/technical_graph/data_layer.rb +58 -0
- data/lib/technical_graph/data_layer_processor.rb +212 -0
- data/lib/technical_graph/graph_image_drawer.rb +22 -20
- data/test/test_technical_autocolor.rb +1 -1
- data/test/test_technical_axis_enlarge.rb +2 -2
- data/test/test_technical_graph.rb +1 -1
- data/test/test_technical_graph_axis.rb +2 -2
- data/test/test_technical_multilayer.rb +1 -1
- data/test/test_technical_simple_graph.rb +1 -1
- data/test/test_technical_smoother.rb +214 -0
- metadata +6 -4
data/README.md
CHANGED
@@ -137,6 +137,10 @@ Layer options Hash
|
|
137
137
|
* layer_options[:label] - label used in legend
|
138
138
|
* layer_options[:color] - color of graph layer, ex.: 'red', 'green', '#FFFF00'
|
139
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
|
140
144
|
|
141
145
|
|
142
146
|
Contributing to technical-graph
|
data/Rakefile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.0
|
data/lib/technical_graph.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
#encoding: utf-8
|
2
2
|
|
3
3
|
require 'technical_graph/graph_color_library'
|
4
|
+
require 'technical_graph/data_layer_processor'
|
4
5
|
|
5
6
|
# Stores only data used for one layer
|
6
7
|
# Instances of this class are used elsewhere
|
@@ -13,12 +14,27 @@ class DataLayer
|
|
13
14
|
|
14
15
|
@data_params[:color] ||= GraphColorLibrary.instance.get_color
|
15
16
|
@data_params[:label] ||= ''
|
17
|
+
# default true, write values near dots
|
18
|
+
@data_params[:value_labels] = false if options[:value_labels] == false
|
19
|
+
|
20
|
+
# smoothing parameters
|
21
|
+
# by default it is false
|
22
|
+
@data_params[:simple_smoother] = true if options[:simple_smoother] == true
|
23
|
+
@data_params[:simple_smoother_level] ||= 3
|
24
|
+
@data_params[:simple_smoother_strategy] ||= DataLayerProcessor::DEFAULT_STRATEGY
|
25
|
+
# was already done
|
26
|
+
@data_params[:processor_finished] = false
|
27
|
+
|
28
|
+
@processor = DataLayerProcessor.new(self)
|
16
29
|
|
17
30
|
# set data and append initial data
|
18
31
|
clear_data
|
19
32
|
append_data(d)
|
20
33
|
end
|
21
34
|
|
35
|
+
# can be used to approximation and other data processing
|
36
|
+
attr_reader :processor
|
37
|
+
|
22
38
|
# Accessor for setting chart data for layer to draw
|
23
39
|
def append_data(d)
|
24
40
|
if d.kind_of? Array
|
@@ -33,6 +49,18 @@ class DataLayer
|
|
33
49
|
# Array of {:x =>, :y =>}
|
34
50
|
attr_reader :data
|
35
51
|
|
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!
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
36
64
|
# Additional parameters
|
37
65
|
attr_reader :data_params
|
38
66
|
|
@@ -49,6 +77,36 @@ class DataLayer
|
|
49
77
|
return @data_params[:label]
|
50
78
|
end
|
51
79
|
|
80
|
+
# Write values near dots
|
81
|
+
def value_labels
|
82
|
+
return @data_params[:value_labels]
|
83
|
+
end
|
84
|
+
|
85
|
+
# Turn on smoothing processor
|
86
|
+
def simple_smoother
|
87
|
+
return @data_params[:simple_smoother]
|
88
|
+
end
|
89
|
+
|
90
|
+
# Smoother level
|
91
|
+
def simple_smoother_level
|
92
|
+
return @data_params[:simple_smoother_level]
|
93
|
+
end
|
94
|
+
|
95
|
+
# Smoother strategy
|
96
|
+
def simple_smoother_strategy
|
97
|
+
return @data_params[:simple_smoother_strategy]
|
98
|
+
end
|
99
|
+
|
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
|
108
|
+
end
|
109
|
+
|
52
110
|
# Clear data
|
53
111
|
def clear_data
|
54
112
|
@data = Array.new
|
@@ -0,0 +1,212 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
|
3
|
+
# Approximate data layer
|
4
|
+
|
5
|
+
class DataLayerProcessor
|
6
|
+
STRATEGIES = {
|
7
|
+
:rectangular => 'generate_vector_rectangular',
|
8
|
+
:gauss => 'generate_vector_gauss'
|
9
|
+
}
|
10
|
+
DEFAULT_STRATEGY = :rectangular
|
11
|
+
|
12
|
+
MIN_LEVEL = 1
|
13
|
+
MAX_LEVEL = 200
|
14
|
+
|
15
|
+
# use 'x' axis for processing also
|
16
|
+
PROCESS_WITH_PARAMETER_DISTANCE = false
|
17
|
+
|
18
|
+
# default Gauss coefficient
|
19
|
+
DEFAULT_GAUSS_COEFF = 0.2
|
20
|
+
|
21
|
+
def initialize(data_layer)
|
22
|
+
@data_layer = data_layer
|
23
|
+
@strategy = DEFAULT_STRATEGY
|
24
|
+
@level = MIN_LEVEL
|
25
|
+
@vector = Array.new
|
26
|
+
@gauss_coeff = DEFAULT_GAUSS_COEFF
|
27
|
+
end
|
28
|
+
|
29
|
+
attr_reader :vector
|
30
|
+
attr_accessor :gauss_coeff
|
31
|
+
|
32
|
+
# Level of approximation
|
33
|
+
def level=(l)
|
34
|
+
@level = l.to_i if l.to_i >= MIN_LEVEL and l.to_i < MAX_LEVEL
|
35
|
+
end
|
36
|
+
|
37
|
+
attr_reader :level
|
38
|
+
|
39
|
+
# Choose other strategy from STRATEGIES
|
40
|
+
def strategy=(s)
|
41
|
+
method = STRATEGIES[s]
|
42
|
+
@strategy = s unless method.nil?
|
43
|
+
end
|
44
|
+
|
45
|
+
attr_reader :strategy
|
46
|
+
|
47
|
+
|
48
|
+
# This vector will be used to process values (Y'es)
|
49
|
+
# Use proper strategy
|
50
|
+
def generate_vector
|
51
|
+
method = STRATEGIES[@strategy]
|
52
|
+
if method.nil?
|
53
|
+
method = STRATEGIES[DEFAULT_STRATEGY]
|
54
|
+
end
|
55
|
+
return self.send(method)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Process values
|
59
|
+
def process
|
60
|
+
t = Time.now
|
61
|
+
old_data = @data_layer.data
|
62
|
+
new_data = Array.new
|
63
|
+
|
64
|
+
(0...old_data.size).each do |i|
|
65
|
+
new_data << {
|
66
|
+
:x => old_data[i][:x],
|
67
|
+
:y => process_part(old_data, i)
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
puts "Smoothing completed, level #{level}, data size #{old_data.size}, time #{Time.now - t}"
|
72
|
+
|
73
|
+
return new_data
|
74
|
+
end
|
75
|
+
|
76
|
+
# Process part (size depends on level)
|
77
|
+
def process_part(old_data, position)
|
78
|
+
# neutral data, used where position is near edge to calculate new value
|
79
|
+
neutral_data = {
|
80
|
+
:x => old_data[position][:x],
|
81
|
+
:y => old_data[position][:y]
|
82
|
+
}
|
83
|
+
part_array = Array.new(level, neutral_data)
|
84
|
+
|
85
|
+
# add data from old_data to part_array
|
86
|
+
offset = (level/2.0).floor
|
87
|
+
# copying data
|
88
|
+
(0...level).each do |l|
|
89
|
+
copy_pos = position + l - offset
|
90
|
+
# if copy_pos is inside data
|
91
|
+
if copy_pos >= 0 and old_data.size > copy_pos
|
92
|
+
part_array[l] = old_data[copy_pos]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
# here we should have part_array and vector
|
96
|
+
# and now do some magic :]
|
97
|
+
|
98
|
+
if PROCESS_WITH_PARAMETER_DISTANCE == false
|
99
|
+
y_sum = 0.0
|
100
|
+
(0...level).each do |l|
|
101
|
+
y_sum += part_array[l][:y] * vector[l]
|
102
|
+
end
|
103
|
+
return y_sum
|
104
|
+
else
|
105
|
+
# TODO bugs!, issues with NaN
|
106
|
+
# waged average using inverted distance
|
107
|
+
_sum = 0.0
|
108
|
+
_wages = 0.0
|
109
|
+
_x_position = old_data[position][:x]
|
110
|
+
|
111
|
+
(0...level).each do |l|
|
112
|
+
_x_distance = (part_array[l][:x] - _x_position).abs
|
113
|
+
_wage = (1.0 / _x_distance)
|
114
|
+
|
115
|
+
unless _wage.nan?
|
116
|
+
_wages += _wage
|
117
|
+
_sum += (part_array[l][:y] * vector[l]) / _x_distance
|
118
|
+
end
|
119
|
+
end
|
120
|
+
y = _sum.to_f / _wages.to_f
|
121
|
+
puts y
|
122
|
+
return y
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
# This vector will be used to process values (Y'es), linear algorithm
|
129
|
+
def generate_vector_rectangular
|
130
|
+
@vector = Array.new
|
131
|
+
# calculated
|
132
|
+
(1..level).each do |i|
|
133
|
+
@vector << 1.0 / level.to_f
|
134
|
+
end
|
135
|
+
return @vector
|
136
|
+
end
|
137
|
+
|
138
|
+
# This vector will be used to process values (Y'es), linear algorithm
|
139
|
+
def generate_vector_gauss
|
140
|
+
# http://www.techotopia.com/index.php/Ruby_Math_Functions_and_Methods#Ruby_Math_Constants
|
141
|
+
# http://pl.wikipedia.org/wiki/Okno_czasowe
|
142
|
+
|
143
|
+
# calculation
|
144
|
+
count = (level.to_f / 2.0).floor + 1
|
145
|
+
|
146
|
+
v = Array.new
|
147
|
+
# calculated
|
148
|
+
(1..count).each do |i|
|
149
|
+
v << Math::E ** ((-0.5) * (i*gauss_coeff) ** 2)
|
150
|
+
end
|
151
|
+
|
152
|
+
@vector = make_mirror(v, level)
|
153
|
+
|
154
|
+
normalize_vector
|
155
|
+
|
156
|
+
return @vector
|
157
|
+
end
|
158
|
+
|
159
|
+
# Multiply vector to have sum eq. 1.0
|
160
|
+
def normalize_vector
|
161
|
+
s = 0.0
|
162
|
+
@vector.each do |v|
|
163
|
+
s += v
|
164
|
+
end
|
165
|
+
|
166
|
+
new_vector = Array.new
|
167
|
+
|
168
|
+
@vector.each do |v|
|
169
|
+
new_vector << v / s
|
170
|
+
end
|
171
|
+
|
172
|
+
@vector = new_vector
|
173
|
+
|
174
|
+
return @vector
|
175
|
+
end
|
176
|
+
|
177
|
+
# Make mirror array
|
178
|
+
# size = 7 => [ i[3], i[2], i[1], i[0], i[1], i[2], i[3] ]
|
179
|
+
# size = 8 => [ i[3], i[2], i[1], i[0], i[0], i[1], i[2], i[3] ]
|
180
|
+
def make_mirror(input, size)
|
181
|
+
a = Array.new(size, 0.1)
|
182
|
+
if size.even?
|
183
|
+
# two 'first' in central
|
184
|
+
c_left = size/2 - 1
|
185
|
+
c_right = size/2
|
186
|
+
|
187
|
+
a[c_left] = input[0]
|
188
|
+
a[c_right] = input[0]
|
189
|
+
else
|
190
|
+
# there is one 'first'
|
191
|
+
c_left = (size/2.0).floor
|
192
|
+
c_right = (size/2.0).floor
|
193
|
+
|
194
|
+
a[c_left] = input[0]
|
195
|
+
# a[c_right] = input[0]
|
196
|
+
end
|
197
|
+
|
198
|
+
# the rest
|
199
|
+
i = 0
|
200
|
+
while c_left > 0
|
201
|
+
i += 1
|
202
|
+
c_left -= 1
|
203
|
+
c_right += 1
|
204
|
+
|
205
|
+
a[c_left] = input[i]
|
206
|
+
a[c_right] = input[i]
|
207
|
+
end
|
208
|
+
|
209
|
+
return a
|
210
|
+
end
|
211
|
+
|
212
|
+
end
|
@@ -215,14 +215,16 @@ class GraphImageDrawer
|
|
215
215
|
end
|
216
216
|
|
217
217
|
# labels
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
218
|
+
if l.value_labels
|
219
|
+
coords.each do |c|
|
220
|
+
string_label = "#{truncate_string % c[:dy]}"
|
221
|
+
layer_text.text(
|
222
|
+
c[:ax] + 5, c[:ay],
|
223
|
+
string_label
|
224
|
+
)
|
225
|
+
end
|
226
|
+
layer_text.draw(@image)
|
224
227
|
end
|
225
|
-
layer_text.draw(@image)
|
226
228
|
|
227
229
|
# lines and circles
|
228
230
|
coords.each do |c|
|
@@ -244,8 +246,8 @@ class GraphImageDrawer
|
|
244
246
|
|
245
247
|
# used for auto positioning of legend
|
246
248
|
if legend_auto_position
|
247
|
-
@drawn_points << {:x => c[:ax], :y => c[:ay]}
|
248
|
-
@drawn_points << {:x => c[:bx], :y => c[:by]}
|
249
|
+
@drawn_points << { :x => c[:ax], :y => c[:ay] }
|
250
|
+
@drawn_points << { :x => c[:bx], :y => c[:by] }
|
249
251
|
end
|
250
252
|
end
|
251
253
|
layer_line.draw(@image)
|
@@ -264,14 +266,14 @@ class GraphImageDrawer
|
|
264
266
|
|
265
267
|
# check 8 places:
|
266
268
|
positions = [
|
267
|
-
{:x => legend_margin, :y => 0 + legend_margin}, # top-left
|
268
|
-
{:x => width/2, :y => 0 + legend_margin}, # top-center
|
269
|
-
{:x => width - legend_margin - legend_width, :y => 0 + legend_margin}, # top-right
|
270
|
-
{:x => legend_margin, :y => height/2}, # middle-left
|
271
|
-
{:x => width - legend_margin - legend_width, :y => height/2}, # middle-right
|
272
|
-
{:x => legend_margin, :y => height - legend_margin - legend_height}, # bottom-left
|
273
|
-
{:x => width/2, :y => height - legend_margin - legend_height}, # bottom-center
|
274
|
-
{:x => width - legend_margin - legend_width, :y => height - legend_margin - legend_height}, # bottom-right
|
269
|
+
{ :x => legend_margin, :y => 0 + legend_margin }, # top-left
|
270
|
+
{ :x => width/2, :y => 0 + legend_margin }, # top-center
|
271
|
+
{ :x => width - legend_margin - legend_width, :y => 0 + legend_margin }, # top-right
|
272
|
+
{ :x => legend_margin, :y => height/2 }, # middle-left
|
273
|
+
{ :x => width - legend_margin - legend_width, :y => height/2 }, # middle-right
|
274
|
+
{ :x => legend_margin, :y => height - legend_margin - legend_height }, # bottom-left
|
275
|
+
{ :x => width/2, :y => height - legend_margin - legend_height }, # bottom-center
|
276
|
+
{ :x => width - legend_margin - legend_width, :y => height - legend_margin - legend_height }, # bottom-right
|
275
277
|
]
|
276
278
|
|
277
279
|
# calculate nearest distance of all drawn points
|
@@ -279,7 +281,7 @@ class GraphImageDrawer
|
|
279
281
|
p[:distance] = (width ** 2 + height ** 2) ** 0.5 # max distance, diagonal of graph
|
280
282
|
@drawn_points.each do |dp|
|
281
283
|
# calculate drawn point distance to being checked now legend position
|
282
|
-
two_points_distance = (
|
284
|
+
two_points_distance = ((p[:x] - dp[:x]) ** 2 + (p[:y] - dp[:y]) ** 2) ** 0.5
|
283
285
|
# modify only if distance is closer
|
284
286
|
if p[:distance] > two_points_distance
|
285
287
|
p[:distance] = two_points_distance
|
@@ -288,7 +290,7 @@ class GraphImageDrawer
|
|
288
290
|
end
|
289
291
|
|
290
292
|
# chose position with hihest distance
|
291
|
-
positions.sort!{|a,b| a[:distance] <=> b[:distance]}
|
293
|
+
positions.sort! { |a, b| a[:distance] <=> b[:distance] }
|
292
294
|
best_position = positions.last
|
293
295
|
options[:legend_x] = best_position[:x]
|
294
296
|
options[:legend_y] = best_position[:y]
|
@@ -318,7 +320,7 @@ class GraphImageDrawer
|
|
318
320
|
|
319
321
|
layers.each do |l|
|
320
322
|
legend_text.fill(l.color)
|
321
|
-
|
323
|
+
|
322
324
|
string_label = l.label
|
323
325
|
legend_text.text(
|
324
326
|
x, y,
|
@@ -58,8 +58,8 @@ class TestTechnicalAxisEnlarge < Test::Unit::TestCase
|
|
58
58
|
|
59
59
|
@tg.render
|
60
60
|
|
61
|
-
@tg.image_drawer.save_to_file('test_axis_enlarge.png')
|
62
|
-
@tg.image_drawer.save_to_file('test_axis_enlarge.svg')
|
61
|
+
@tg.image_drawer.save_to_file('samples/tests/test_axis_enlarge.png')
|
62
|
+
@tg.image_drawer.save_to_file('samples/tests/test_axis_enlarge.svg')
|
63
63
|
@tg.image_drawer.to_png.class.should == String
|
64
64
|
|
65
65
|
end
|
@@ -67,7 +67,7 @@ class TestTechnicalGraph < Test::Unit::TestCase
|
|
67
67
|
layer.data.size.should == 2 * @data_size
|
68
68
|
|
69
69
|
# @tg.render
|
70
|
-
# @tg.image.save_to_file('test1.png')
|
70
|
+
# @tg.image.save_to_file('samples/tests/test1.png')
|
71
71
|
end
|
72
72
|
|
73
73
|
should 'has ability to filter records with similar x\'es' do
|
@@ -262,7 +262,7 @@ class TestTechnicalGraphAxis < Test::Unit::TestCase
|
|
262
262
|
@tg.axis.parameter_axis.should == [-8.0, -6.0, -4.0, -2.0, 0.0, 2.0, 4.0, 6.0]
|
263
263
|
@tg.axis.value_axis.should == [-4.0, -2.0, 0.0, 2.0]
|
264
264
|
|
265
|
-
@tg.image_drawer.save_to_file('test1.png')
|
265
|
+
@tg.image_drawer.save_to_file('samples/tests/test1.png')
|
266
266
|
end
|
267
267
|
|
268
268
|
|
@@ -297,7 +297,7 @@ class TestTechnicalGraphAxis < Test::Unit::TestCase
|
|
297
297
|
|
298
298
|
@tg.render
|
299
299
|
|
300
|
-
@tg.image_drawer.save_to_file('test2.png')
|
300
|
+
@tg.image_drawer.save_to_file('samples/tests/test2.png')
|
301
301
|
end
|
302
302
|
|
303
303
|
end
|
@@ -0,0 +1,214 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestTechnicalSmoother < Test::Unit::TestCase
|
4
|
+
context 'calculations' do
|
5
|
+
setup do
|
6
|
+
max = 500
|
7
|
+
|
8
|
+
# adding simple layer
|
9
|
+
@layer_params = {
|
10
|
+
:antialias => true,
|
11
|
+
:label => 'first',
|
12
|
+
:value_labels => false,
|
13
|
+
:simple_smother => 4
|
14
|
+
}
|
15
|
+
@layer_data = Array.new
|
16
|
+
(0..max).each do |i|
|
17
|
+
x = -10.0 + (20.0 * i.to_f / max.to_f)
|
18
|
+
y = 10.0 * Math.cos(i.to_f * (0.5 * 3.14 / max.to_f))
|
19
|
+
|
20
|
+
y += rand
|
21
|
+
x += rand / max.to_f
|
22
|
+
|
23
|
+
@layer_data << { :x => x, :y => y }
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
@data_layer = DataLayer.new(@layer_data, @layer_params)
|
28
|
+
@processor = @data_layer.processor
|
29
|
+
end
|
30
|
+
|
31
|
+
should 'do some basic tests' do
|
32
|
+
@processor.should.kind_of? DataLayerProcessor
|
33
|
+
end
|
34
|
+
|
35
|
+
should 'calculated vector has proper size and sum eq. 1.0' do
|
36
|
+
@processor.generate_vector.should.kind_of? Array
|
37
|
+
@processor.generate_vector.size.should == @processor.level
|
38
|
+
|
39
|
+
DataLayerProcessor::STRATEGIES.keys.each do |s|
|
40
|
+
@processor.strategy = s
|
41
|
+
@processor.strategy.should == s
|
42
|
+
|
43
|
+
(1...10).each do |i|
|
44
|
+
@processor.level = i
|
45
|
+
@processor.level.should == i
|
46
|
+
|
47
|
+
@processor.generate_vector.size.should == @processor.level
|
48
|
+
|
49
|
+
s = 0.0
|
50
|
+
@processor.generate_vector.each do |t|
|
51
|
+
s += t
|
52
|
+
end
|
53
|
+
s.should be_within(0.01).of(1.0)
|
54
|
+
|
55
|
+
# puts @processor.vector.inspect
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
should 'processed data has the same size that old one' do
|
62
|
+
DataLayerProcessor::STRATEGIES.keys.each do |s|
|
63
|
+
@processor.strategy = s
|
64
|
+
@processor.strategy.should == s
|
65
|
+
(1...9).each do |i|
|
66
|
+
@processor.level = i
|
67
|
+
@processor.level.should == i
|
68
|
+
|
69
|
+
@processor.generate_vector.size.should == @processor.level
|
70
|
+
|
71
|
+
new_data = @processor.process
|
72
|
+
new_data.size.should == @data_layer.data.size
|
73
|
+
|
74
|
+
# add as new layer
|
75
|
+
#@data_layer = DataLayer.new(@layer_data, @layer_params)
|
76
|
+
# @tg.add_layer(new_data, @layer_params)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
should 'create simple graph with unprocessed and processed layer (gauss)' do
|
82
|
+
tg = TechnicalGraph.new(
|
83
|
+
{
|
84
|
+
:width => 2000,
|
85
|
+
:height => 1500,
|
86
|
+
}
|
87
|
+
)
|
88
|
+
max = 1000
|
89
|
+
|
90
|
+
# adding simple layer
|
91
|
+
layer_params = {
|
92
|
+
:antialias => true,
|
93
|
+
:color => 'red',
|
94
|
+
:label => 'raw',
|
95
|
+
:value_labels => false,
|
96
|
+
# :simple_smother => 8
|
97
|
+
}
|
98
|
+
layer_data = Array.new
|
99
|
+
(0..max).each do |i|
|
100
|
+
x = -10.0 + (20.0 * i.to_f / max.to_f)
|
101
|
+
y = 10.0 * Math.cos(i.to_f * (0.5 * 3.14 / max.to_f))
|
102
|
+
|
103
|
+
y += rand
|
104
|
+
x += rand / max.to_f
|
105
|
+
|
106
|
+
layer_data << { :x => x, :y => y }
|
107
|
+
end
|
108
|
+
|
109
|
+
# non processed
|
110
|
+
tg.add_layer(layer_data, layer_params)
|
111
|
+
|
112
|
+
# process and add
|
113
|
+
approx = layer_data_b = tg.layers[0].processor
|
114
|
+
approx.strategy = :gauss
|
115
|
+
approx.level = 9
|
116
|
+
approx.generate_vector
|
117
|
+
|
118
|
+
layer_data_b = approx.process
|
119
|
+
layer_params_b = {
|
120
|
+
:antialias => false,
|
121
|
+
:color => 'blue',
|
122
|
+
:label => 'processed',
|
123
|
+
:value_labels => false,
|
124
|
+
# :simple_smother => 9
|
125
|
+
}
|
126
|
+
tg.add_layer(layer_data_b, layer_params_b)
|
127
|
+
|
128
|
+
tg.render
|
129
|
+
tg.image_drawer.save_to_file('samples/tests/test_simple_gauss.png')
|
130
|
+
end
|
131
|
+
|
132
|
+
should 'create simple graph using only layer params' do
|
133
|
+
tg = TechnicalGraph.new(
|
134
|
+
{
|
135
|
+
:width => 5000,
|
136
|
+
:height => 3000,
|
137
|
+
|
138
|
+
:legend => true,
|
139
|
+
:legend_auto => true,
|
140
|
+
:legend_width => 90,
|
141
|
+
:legend_margin => 60,
|
142
|
+
:legend_x => 50,
|
143
|
+
:legend_y => 50,
|
144
|
+
}
|
145
|
+
)
|
146
|
+
max = 2000
|
147
|
+
|
148
|
+
layer_data = Array.new
|
149
|
+
(0..max).each do |i|
|
150
|
+
x = -10.0 + (20.0 * i.to_f / max.to_f)
|
151
|
+
y = 10.0 * Math.cos(i.to_f * (2.0 * 3.14 / max.to_f))
|
152
|
+
|
153
|
+
y += rand * 4.0
|
154
|
+
x += rand / max.to_f
|
155
|
+
|
156
|
+
layer_data << { :x => x, :y => y }
|
157
|
+
end
|
158
|
+
|
159
|
+
# adding simple layer
|
160
|
+
layer_params = {
|
161
|
+
:antialias => false,
|
162
|
+
:color => 'red',
|
163
|
+
:label => 'raw',
|
164
|
+
:value_labels => false,
|
165
|
+
:simple_smoother => true,
|
166
|
+
:simple_smoother_level => 1,
|
167
|
+
:simple_smoother_strategy => :gauss
|
168
|
+
}
|
169
|
+
#tg.add_layer(layer_data.clone, layer_params)
|
170
|
+
#layer_params_b = layer_params.merge({
|
171
|
+
# :color => 'blue',
|
172
|
+
# :label => 'processed - level 3',
|
173
|
+
# :simple_smoother_level => 3,
|
174
|
+
# :simple_smoother => true
|
175
|
+
#})
|
176
|
+
#tg.add_layer(layer_data.clone, layer_params)
|
177
|
+
layer_params_c = layer_params.clone.merge({
|
178
|
+
:color => 'green',
|
179
|
+
:label => 'processed - level 100',
|
180
|
+
:simple_smoother_level => 100,
|
181
|
+
:simple_smoother => true
|
182
|
+
})
|
183
|
+
#tg.add_layer(layer_data.clone, layer_params)
|
184
|
+
#layer_params_d = layer_params.clone.merge({
|
185
|
+
# :color => 'brown',
|
186
|
+
# :label => 'processed - level 50',
|
187
|
+
# :simple_smoother_level => 50,
|
188
|
+
# :simple_smoother => true
|
189
|
+
#})
|
190
|
+
tg.add_layer(layer_data.clone, layer_params)
|
191
|
+
#tg.add_layer(layer_data.clone, layer_params_b)
|
192
|
+
tg.add_layer(layer_data.clone, layer_params_c)
|
193
|
+
#tg.add_layer(layer_data.clone, layer_params_d)
|
194
|
+
|
195
|
+
|
196
|
+
layer_params_e = layer_params.clone.merge({
|
197
|
+
:color => 'blue',
|
198
|
+
:label => 'processed (rectangular) - level 100',
|
199
|
+
:simple_smoother_level => 100,
|
200
|
+
:simple_smoother => true,
|
201
|
+
:simple_smoother_strategy => :rectangular
|
202
|
+
})
|
203
|
+
tg.add_layer(layer_data.clone, layer_params_e)
|
204
|
+
|
205
|
+
|
206
|
+
tg.render
|
207
|
+
tg.image_drawer.save_to_file('samples/tests/test_smoothing_multiple.png')
|
208
|
+
end
|
209
|
+
|
210
|
+
|
211
|
+
end
|
212
|
+
|
213
|
+
|
214
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: technical_graph
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 19
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 3
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.3.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Aleksander Kwiatkowski
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-10-
|
18
|
+
date: 2011-10-12 00:00:00 +02:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -122,6 +122,7 @@ files:
|
|
122
122
|
- VERSION
|
123
123
|
- lib/technical_graph.rb
|
124
124
|
- lib/technical_graph/data_layer.rb
|
125
|
+
- lib/technical_graph/data_layer_processor.rb
|
125
126
|
- lib/technical_graph/graph_axis.rb
|
126
127
|
- lib/technical_graph/graph_color_library.rb
|
127
128
|
- lib/technical_graph/graph_data_processor.rb
|
@@ -133,6 +134,7 @@ files:
|
|
133
134
|
- test/test_technical_graph_axis.rb
|
134
135
|
- test/test_technical_multilayer.rb
|
135
136
|
- test/test_technical_simple_graph.rb
|
137
|
+
- test/test_technical_smoother.rb
|
136
138
|
has_rdoc: true
|
137
139
|
homepage: http://github.com/akwiatkowski/technical_graph
|
138
140
|
licenses:
|