technical_graph 0.3.2 → 0.4.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/DOCUMENTATION.md +14 -4
- data/DOCUMENTATION.textile +68 -1
- data/Gemfile +1 -0
- data/Gemfile.lock +2 -0
- data/VERSION +1 -1
- data/lib/technical_graph/array.rb +25 -0
- data/lib/technical_graph/data_layer.rb +45 -7
- data/lib/technical_graph/data_layer_processor.rb +4 -1
- data/lib/technical_graph/data_layer_processor_noise_removal.rb +4 -23
- data/lib/technical_graph/data_layer_processor_simple_smoother.rb +9 -3
- data/lib/technical_graph/graph_axis.rb +69 -217
- data/lib/technical_graph/graph_color_library.rb +38 -22
- data/lib/technical_graph/graph_data_processor.rb +13 -1
- data/lib/technical_graph/graph_image_drawer.rb +97 -114
- data/lib/technical_graph/graph_image_drawer_module.rb +72 -0
- data/lib/technical_graph/graph_image_drawer_rasem.rb +185 -0
- data/lib/technical_graph/graph_image_drawer_rmagick.rb +237 -0
- data/lib/technical_graph.rb +23 -8
- data/test/helper.rb +4 -0
- data/test/test_technical_autocolor.rb +2 -2
- data/test/test_technical_axis_enlarge.rb +2 -3
- data/test/test_technical_graph.rb +4 -3
- data/test/test_technical_graph_axis.rb +2 -2
- data/test/test_technical_multilayer.rb +2 -2
- data/test/test_technical_rasem.rb +22 -0
- data/test/test_technical_readme.rb +39 -18
- data/test/test_technical_simple_graph.rb +2 -2
- data/test/test_technical_smoother.rb +2 -2
- data/test/test_technical_smoother_adv.rb +15 -5
- metadata +32 -14
data/DOCUMENTATION.md
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
Options Hash
|
4
4
|
|
5
|
+
options[:drawer_class] = :rasem (default) or :rmagick
|
6
|
+
|
5
7
|
Default ranges:
|
6
8
|
|
7
9
|
options[:x_min]
|
@@ -38,6 +40,13 @@ Labels has truncate string to define precision. Default it is "%.2f".
|
|
38
40
|
|
39
41
|
options[:truncate_string] = "%.2f"
|
40
42
|
|
43
|
+
Draw axis labels, and labels for zero axis
|
44
|
+
|
45
|
+
options[:axis_value_and_param_labels] = true
|
46
|
+
options[:axis_zero_labels] = true
|
47
|
+
|
48
|
+
|
49
|
+
|
41
50
|
Graph image size:
|
42
51
|
|
43
52
|
options[:width]
|
@@ -49,13 +58,13 @@ Possible #RRGGBB or color names (ex. 'white').
|
|
49
58
|
|
50
59
|
options[:background_color] - background color of image
|
51
60
|
options[:background_hatch_color] - background hatch color
|
52
|
-
options[:axis_color] - color of axis
|
61
|
+
options[:axis_color] - color of axis, default #000000
|
53
62
|
|
54
63
|
Anti-aliasing:
|
55
64
|
|
56
|
-
options[:
|
57
|
-
options[:layers_antialias] - use anti-aliasing for data layers, default false, can be override using layer option
|
58
|
-
options[:font_antialias] - use anti-aliasing for all fonts, default false
|
65
|
+
options[:antialias] - draw axis using antialias
|
66
|
+
# options[:layers_antialias] - use anti-aliasing for data layers, default false, can be override using layer option
|
67
|
+
# options[:font_antialias] - use anti-aliasing for all fonts, default false
|
59
68
|
|
60
69
|
Font size:
|
61
70
|
|
@@ -91,3 +100,4 @@ Layer options Hash
|
|
91
100
|
layer_options[:noise_removal] - enable removal of noises/peaks, default false
|
92
101
|
layer_options[:noise_removal_level] - tolerance level, higher - less points will be removes, default 3
|
93
102
|
layer_options[:noise_removal_window_size] - how many near values check for determining what is noise, default 10
|
103
|
+
layer_options[:perform_parameter_uniq] - it takes some time and rarely usable, so it is turned off by default
|
data/DOCUMENTATION.textile
CHANGED
@@ -359,9 +359,76 @@ file_name = 'samples/readme/08b_truncate_string.png'
|
|
359
359
|
!https://github.com/akwiatkowski/technical_graph/raw/master/samples/readme/08b_truncate_string.png((08b) displaying float numbers)!
|
360
360
|
|
361
361
|
|
362
|
+
|
363
|
+
|
364
|
+
|
365
|
+
|
366
|
+
h2. Graph image size
|
367
|
+
|
368
|
+
p. It would be very silly if this library had hard coded image size. You can change it using options[:width] and options[:height].
|
369
|
+
|
370
|
+
<pre>
|
371
|
+
<code>
|
372
|
+
@simple_data_array = [
|
373
|
+
{ :x => 0, :y => 0 },
|
374
|
+
{ :x => 1, :y => 1 },
|
375
|
+
{ :x => 2, :y => 2 },
|
376
|
+
{ :x => 3, :y => 2 },
|
377
|
+
{ :x => 4, :y => 1 },
|
378
|
+
{ :x => 5, :y => 0 },
|
379
|
+
]
|
380
|
+
|
381
|
+
@tg = TechnicalGraph.new(
|
382
|
+
{
|
383
|
+
:width => 600,
|
384
|
+
:height => 300
|
385
|
+
})
|
386
|
+
@tg.add_layer(@simple_data_array)
|
387
|
+
@tg.render
|
388
|
+
file_name = 'samples/readme/09_image_size.png'
|
389
|
+
@tg.image_drawer.save_to_file(file_name)
|
390
|
+
</code>
|
391
|
+
</pre>
|
392
|
+
|
393
|
+
!https://github.com/akwiatkowski/technical_graph/raw/master/samples/readme/09_image_size.png((09) image size)!
|
394
|
+
|
395
|
+
|
396
|
+
|
397
|
+
h2. Colours
|
398
|
+
|
399
|
+
p. If you think you have better artistic taste feel free to change colours used in graph :)
|
400
|
+
|
401
|
+
<pre>
|
402
|
+
<code>
|
403
|
+
@simple_data_array = [
|
404
|
+
{ :x => 0, :y => 0 },
|
405
|
+
{ :x => 1, :y => 1 },
|
406
|
+
{ :x => 2, :y => 2 },
|
407
|
+
{ :x => 3, :y => 2 },
|
408
|
+
{ :x => 4, :y => 1 },
|
409
|
+
{ :x => 5, :y => 0 },
|
410
|
+
]
|
411
|
+
|
412
|
+
@tg = TechnicalGraph.new(
|
413
|
+
{
|
414
|
+
:width => 600,
|
415
|
+
:height => 300
|
416
|
+
})
|
417
|
+
@tg.add_layer(@simple_data_array, @layer_params)
|
418
|
+
@tg.render
|
419
|
+
file_name = 'samples/readme/09_image_size.png'
|
420
|
+
@tg.image_drawer.save_to_file(file_name)
|
421
|
+
</code>
|
422
|
+
</pre>
|
423
|
+
|
424
|
+
!https://github.com/akwiatkowski/technical_graph/raw/master/samples/readme/09_image_size.png((09) image size)!
|
425
|
+
|
426
|
+
|
427
|
+
|
428
|
+
|
429
|
+
|
362
430
|
h2. TODO
|
363
431
|
|
364
|
-
# Graph image size
|
365
432
|
# Graph colors: background, hatch (option to turn it off?), axis
|
366
433
|
# Anti-aliasing: image size comparison, layer antialiases
|
367
434
|
# Font sizes
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.4.0
|
@@ -11,4 +11,29 @@ class Array
|
|
11
11
|
def float_mean
|
12
12
|
float_sum / size
|
13
13
|
end
|
14
|
+
|
15
|
+
# Create partial array and fill with border values if needed
|
16
|
+
def clone_partial_w_fill(_from, _to)
|
17
|
+
part_array = Array.new
|
18
|
+
# border = false
|
19
|
+
|
20
|
+
(_from.._to).each do |current_i|
|
21
|
+
# outside ranges
|
22
|
+
if current_i < 0
|
23
|
+
part_array << self.first
|
24
|
+
# border = true
|
25
|
+
next
|
26
|
+
end
|
27
|
+
|
28
|
+
if self.size <= current_i
|
29
|
+
part_array << self.last
|
30
|
+
# border = true
|
31
|
+
next
|
32
|
+
end
|
33
|
+
|
34
|
+
part_array << self[current_i]
|
35
|
+
end
|
36
|
+
|
37
|
+
return part_array
|
38
|
+
end
|
14
39
|
end
|
@@ -10,7 +10,23 @@ require 'technical_graph/data_layer_processor'
|
|
10
10
|
|
11
11
|
class DataLayer
|
12
12
|
|
13
|
-
|
13
|
+
# Use global logger for technical_graph or create new
|
14
|
+
def logger
|
15
|
+
return @logger if not @logger.nil?
|
16
|
+
|
17
|
+
if not @technical_graph.nil?
|
18
|
+
@logger = @technical_graph.logger
|
19
|
+
else
|
20
|
+
@logger = Logger.new(STDOUT)
|
21
|
+
end
|
22
|
+
|
23
|
+
@logger
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(d = [], options = { }, technical_graph = nil)
|
27
|
+
# used for accessing logger
|
28
|
+
@technical_graph = technical_graph
|
29
|
+
|
14
30
|
@data_params = options
|
15
31
|
|
16
32
|
@data_params[:color] ||= GraphColorLibrary.instance.get_color
|
@@ -42,10 +58,14 @@ class DataLayer
|
|
42
58
|
if data_array.kind_of? Array
|
43
59
|
# append as DataPoint
|
44
60
|
# convert to DataPoints, which has more specialized methods
|
61
|
+
|
62
|
+
t = Time.now
|
45
63
|
data_array.each do |d|
|
46
64
|
@data << DataPoint.new(d)
|
47
65
|
end
|
48
|
-
|
66
|
+
logger.debug "appending data, size #{data_array.size}"
|
67
|
+
logger.debug " TIME COST #{Time.now - t}"
|
68
|
+
|
49
69
|
# sort, clean bad records
|
50
70
|
process_data_internal
|
51
71
|
|
@@ -72,8 +92,11 @@ class DataLayer
|
|
72
92
|
|
73
93
|
# Run external processor (smoothing, ...)
|
74
94
|
def process!
|
95
|
+
t = Time.now
|
75
96
|
@processed_data = @data.clone
|
76
97
|
@processed_data = @processor.process
|
98
|
+
logger.debug "processed data using external processor"
|
99
|
+
logger.debug " TIME COST #{Time.now - t}"
|
77
100
|
end
|
78
101
|
|
79
102
|
# Additional parameters
|
@@ -84,10 +107,6 @@ class DataLayer
|
|
84
107
|
return @data_params[:color]
|
85
108
|
end
|
86
109
|
|
87
|
-
def antialias
|
88
|
-
return @data_params[:antialias]
|
89
|
-
end
|
90
|
-
|
91
110
|
def label
|
92
111
|
return @data_params[:label]
|
93
112
|
end
|
@@ -116,6 +135,10 @@ class DataLayer
|
|
116
135
|
return @data_params[:simple_smoother_x]
|
117
136
|
end
|
118
137
|
|
138
|
+
def perform_parameter_uniq
|
139
|
+
return @data_params[:perform_parameter_uniq] == true
|
140
|
+
end
|
141
|
+
|
119
142
|
# Clear data
|
120
143
|
def clear_data
|
121
144
|
@data = Array.new
|
@@ -123,12 +146,24 @@ class DataLayer
|
|
123
146
|
|
124
147
|
# Clean and process data used for drawing current data layer
|
125
148
|
def process_data_internal
|
149
|
+
t = Time.now
|
150
|
+
|
126
151
|
# delete duplicates
|
127
|
-
|
152
|
+
if perform_parameter_uniq
|
153
|
+
@data = @data.inject([]) { |result, d| result << d unless result.select { |r| r.x == d.x }.size > 0; result }
|
154
|
+
end
|
155
|
+
|
156
|
+
logger.debug "internal processor - deleting duplicates"
|
157
|
+
logger.debug " TIME COST #{Time.now - t}"
|
158
|
+
t = Time.now
|
128
159
|
|
129
160
|
@data.delete_if { |d| d.x.nil? or d.y.nil? }
|
130
161
|
@data.sort! { |d, e| d.x <=> e.x }
|
131
162
|
|
163
|
+
logger.debug "internal processor - deleting nils and sorting"
|
164
|
+
logger.debug " TIME COST #{Time.now - t}"
|
165
|
+
t = Time.now
|
166
|
+
|
132
167
|
# default X values, if data is not empty
|
133
168
|
if @data.size > 0
|
134
169
|
@data_params[:x_min] = @data.first.x || @options[:default_x_min]
|
@@ -139,6 +174,9 @@ class DataLayer
|
|
139
174
|
@data_params[:y_min] = y_sort.first.y || @options[:default_y_min]
|
140
175
|
@data_params[:y_max] = y_sort.last.y || @options[:@default_y_max]
|
141
176
|
end
|
177
|
+
|
178
|
+
logger.debug "internal processor - setting min and max"
|
179
|
+
logger.debug " TIME COST #{Time.now - t}"
|
142
180
|
end
|
143
181
|
|
144
182
|
def x_min
|
@@ -9,6 +9,10 @@ class DataLayerProcessor
|
|
9
9
|
include DataLayerProcessorSimpleSmoother
|
10
10
|
include DataLayerProcessorNoiseRemoval
|
11
11
|
|
12
|
+
def logger
|
13
|
+
@data_layer.logger
|
14
|
+
end
|
15
|
+
|
12
16
|
def initialize(data_layer)
|
13
17
|
@data_layer = data_layer
|
14
18
|
simple_smoother_initialize(data_params)
|
@@ -33,7 +37,6 @@ class DataLayerProcessor
|
|
33
37
|
simple_smoother_initialize(data_params)
|
34
38
|
noise_removal_initialize(data_params)
|
35
39
|
|
36
|
-
# TODO add in options array to choose order of these methods
|
37
40
|
noise_removal_process
|
38
41
|
simple_smoother_process
|
39
42
|
|
@@ -23,7 +23,7 @@ module DataLayerProcessorNoiseRemoval
|
|
23
23
|
|
24
24
|
@noises_removed_count = 0
|
25
25
|
|
26
|
-
|
26
|
+
logger.debug "Noise removal started"
|
27
27
|
|
28
28
|
(0...data.size).each do |i|
|
29
29
|
if not noise?(i)
|
@@ -33,7 +33,8 @@ module DataLayerProcessorNoiseRemoval
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
|
36
|
+
logger.debug "Noise removal completed, removed #{@noises_removed_count}"
|
37
|
+
logger.debug " TIME COST #{Time.now - t}"
|
37
38
|
|
38
39
|
@data = new_data
|
39
40
|
return new_data
|
@@ -52,27 +53,7 @@ module DataLayerProcessorNoiseRemoval
|
|
52
53
|
i_from = noise_removal_window_from(i)
|
53
54
|
i_to = noise_removal_window_to(i)
|
54
55
|
|
55
|
-
|
56
|
-
part_array = Array.new
|
57
|
-
border = false
|
58
|
-
|
59
|
-
(i_from..i_to).each do |current_i|
|
60
|
-
# outside ranges
|
61
|
-
if current_i < 0
|
62
|
-
part_array << data.first
|
63
|
-
border = true
|
64
|
-
next
|
65
|
-
end
|
66
|
-
|
67
|
-
if data.size <= current_i
|
68
|
-
part_array << data.last
|
69
|
-
border = true
|
70
|
-
next
|
71
|
-
end
|
72
|
-
|
73
|
-
part_array << data[current_i]
|
74
|
-
|
75
|
-
end
|
56
|
+
part_array = data.clone_partial_w_fill(i_from, i_to)
|
76
57
|
y_mean = part_array.collect { |p| p.y }.float_mean
|
77
58
|
|
78
59
|
# another algorithm
|
@@ -72,7 +72,8 @@ module DataLayerProcessorSimpleSmoother
|
|
72
72
|
|
73
73
|
# pre-processing, distance
|
74
74
|
if simple_smoother_x == true
|
75
|
-
|
75
|
+
logger.debug "X axis distance smoothing enabled"
|
76
|
+
t = Time.now
|
76
77
|
|
77
78
|
(0...old_data.size).each do |i|
|
78
79
|
new_data << DataPoint.xy(old_data[i].x, process_part(old_data, i, false))
|
@@ -80,15 +81,20 @@ module DataLayerProcessorSimpleSmoother
|
|
80
81
|
|
81
82
|
old_data = new_data
|
82
83
|
new_data = Array.new
|
84
|
+
|
85
|
+
logger.debug "X axis distance smoothing completed"
|
86
|
+
logger.debug " TIME COST #{Time.now - t}"
|
83
87
|
end
|
84
88
|
|
85
|
-
|
89
|
+
logger.debug "Y axis distance smoothing"
|
90
|
+
t = Time.now
|
86
91
|
|
87
92
|
(0...old_data.size).each do |i|
|
88
93
|
new_data << DataPoint.xy(old_data[i].x, process_part(old_data, i))
|
89
94
|
end
|
90
95
|
|
91
|
-
|
96
|
+
logger.debug "Y axis Smoothing completed, simple_smoother_level #{simple_smoother_level}, data size #{old_data.size}"
|
97
|
+
logger.debug " TIME COST #{Time.now - t}"
|
92
98
|
|
93
99
|
@data = new_data
|
94
100
|
return new_data
|