technical_graph 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.idea/dictionaries/olek.xml +8 -0
- data/.rvmrc +1 -0
- data/Gemfile +14 -0
- data/Gemfile.lock +32 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +19 -0
- data/Rakefile +55 -0
- data/VERSION +1 -0
- data/lib/technical_graph/data_layer.rb +87 -0
- data/lib/technical_graph/graph_axis.rb +248 -0
- data/lib/technical_graph/graph_data_processor.rb +175 -0
- data/lib/technical_graph/graph_image_drawer.rb +190 -0
- data/lib/technical_graph/refactoring_backup/lib/technical_graph/axis_layer.rb +151 -0
- data/lib/technical_graph/refactoring_backup/lib/technical_graph/axis_layer_draw_module.rb +145 -0
- data/lib/technical_graph/refactoring_backup/lib/technical_graph.rb +55 -0
- data/lib/technical_graph/refactoring_backup/test/test_technical_graph.rb +104 -0
- data/lib/technical_graph/refactoring_backup/test/test_technical_graph_axis.rb +306 -0
- data/lib/technical_graph.rb +55 -0
- data/samples/1.png +0 -0
- data/test/helper.rb +19 -0
- data/test/test_technical_graph.rb +104 -0
- data/test/test_technical_graph_axis.rb +306 -0
- data/test/test_technical_simple_graph.rb +46 -0
- metadata +190 -0
data/.document
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use 1.8.7@technical_graph --create
|
data/Gemfile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
gem 'rmagick'
|
4
|
+
gem 'jeweler'
|
5
|
+
|
6
|
+
# Add dependencies to develop your gem here.
|
7
|
+
# Include everything needed to run rake, tests, features, etc.
|
8
|
+
group :development do
|
9
|
+
gem "shoulda"
|
10
|
+
gem "bundler", "~> 1.0.0"
|
11
|
+
gem "rspec"
|
12
|
+
gem "jeweler" #, "~> 1.6.4"
|
13
|
+
gem "rcov", ">= 0"
|
14
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
diff-lcs (1.1.3)
|
5
|
+
git (1.2.5)
|
6
|
+
jeweler (1.6.4)
|
7
|
+
bundler (~> 1.0)
|
8
|
+
git (>= 1.2.5)
|
9
|
+
rake
|
10
|
+
rake (0.9.2)
|
11
|
+
rcov (0.9.10)
|
12
|
+
rmagick (2.13.1)
|
13
|
+
rspec (2.6.0)
|
14
|
+
rspec-core (~> 2.6.0)
|
15
|
+
rspec-expectations (~> 2.6.0)
|
16
|
+
rspec-mocks (~> 2.6.0)
|
17
|
+
rspec-core (2.6.4)
|
18
|
+
rspec-expectations (2.6.0)
|
19
|
+
diff-lcs (~> 1.1.2)
|
20
|
+
rspec-mocks (2.6.0)
|
21
|
+
shoulda (2.11.3)
|
22
|
+
|
23
|
+
PLATFORMS
|
24
|
+
ruby
|
25
|
+
|
26
|
+
DEPENDENCIES
|
27
|
+
bundler (~> 1.0.0)
|
28
|
+
jeweler
|
29
|
+
rcov
|
30
|
+
rmagick
|
31
|
+
rspec
|
32
|
+
shoulda
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Aleksander Kwiatkowski
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
= technical-graph
|
2
|
+
|
3
|
+
Create neat graphs.
|
4
|
+
|
5
|
+
== Contributing to technical-graph
|
6
|
+
|
7
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
8
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
9
|
+
* Fork the project
|
10
|
+
* Start a feature/bugfix branch
|
11
|
+
* Commit and push until you are happy with your contribution
|
12
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
13
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
14
|
+
|
15
|
+
== Copyright
|
16
|
+
|
17
|
+
Copyright (c) 2011 Aleksander Kwiatkowski. See LICENSE.txt for
|
18
|
+
further details.
|
19
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "technical_graph"
|
18
|
+
gem.homepage = "http://github.com/akwiatkowski/technical_graph"
|
19
|
+
gem.license = "LGPLv3"
|
20
|
+
gem.summary = %Q{Create simple and neat graphs}
|
21
|
+
gem.description = %Q{Create simple and neat graphs}
|
22
|
+
gem.email = "bobikx@poczta.fm"
|
23
|
+
gem.authors = ["Aleksander Kwiatkowski"]
|
24
|
+
|
25
|
+
#gem.files = FileList["[A-Z]*", "{bin,generators,lib,test}/**/*", 'lib/jeweler/templates/.gitignore']
|
26
|
+
# dependencies defined in Gemfile
|
27
|
+
end
|
28
|
+
Jeweler::RubygemsDotOrgTasks.new
|
29
|
+
|
30
|
+
require 'rake/testtask'
|
31
|
+
Rake::TestTask.new(:test) do |test|
|
32
|
+
test.libs << 'lib' << 'test'
|
33
|
+
test.pattern = 'test/**/test_*.rb'
|
34
|
+
test.verbose = true
|
35
|
+
end
|
36
|
+
|
37
|
+
require 'rcov/rcovtask'
|
38
|
+
Rcov::RcovTask.new do |test|
|
39
|
+
test.libs << 'test'
|
40
|
+
test.pattern = 'test/**/test_*.rb'
|
41
|
+
test.verbose = true
|
42
|
+
test.rcov_opts << '--exclude "gems/*"'
|
43
|
+
end
|
44
|
+
|
45
|
+
task :default => :test
|
46
|
+
|
47
|
+
require 'rake/rdoctask'
|
48
|
+
Rake::RDocTask.new do |rdoc|
|
49
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
50
|
+
|
51
|
+
rdoc.rdoc_dir = 'rdoc'
|
52
|
+
rdoc.title = "technical-graph #{version}"
|
53
|
+
rdoc.rdoc_files.include('README*')
|
54
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
55
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.0
|
@@ -0,0 +1,87 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
|
3
|
+
# Stores only data used for one layer
|
4
|
+
# Instances of this class are used elsewhere
|
5
|
+
# Stores also drawing parameters for one layer
|
6
|
+
|
7
|
+
class DataLayer
|
8
|
+
|
9
|
+
def initialize(d = [], options = { })
|
10
|
+
@options = options
|
11
|
+
@data_params = Hash.new
|
12
|
+
|
13
|
+
# set data and append initial data
|
14
|
+
clear_data
|
15
|
+
append_data(d)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Accessor for setting chart data for layer to draw
|
19
|
+
def append_data(d)
|
20
|
+
if d.kind_of? Array
|
21
|
+
@data += d
|
22
|
+
# sort, clean bad records
|
23
|
+
process_data
|
24
|
+
else
|
25
|
+
raise 'Data not an Array'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Array of {:x =>, :y =>}
|
30
|
+
attr_reader :data
|
31
|
+
|
32
|
+
# Additional parameters
|
33
|
+
attr_reader :data_params
|
34
|
+
|
35
|
+
# Color of
|
36
|
+
def color
|
37
|
+
return @data_params[:color] || 'blue'
|
38
|
+
end
|
39
|
+
|
40
|
+
def antialias
|
41
|
+
return @data_params[:antialias] == false
|
42
|
+
end
|
43
|
+
|
44
|
+
# Clear data
|
45
|
+
def clear_data
|
46
|
+
@data = Array.new
|
47
|
+
end
|
48
|
+
|
49
|
+
# Clean and process data used for drawing current data layer
|
50
|
+
def process_data
|
51
|
+
# delete duplicates
|
52
|
+
@data = @data.inject([]) { |result, d| result << d unless result.select { |r| r[:x] == d[:x] }.size > 0; result }
|
53
|
+
|
54
|
+
@data.delete_if { |d| d[:x].nil? or d[:y].nil? }
|
55
|
+
@data.sort! { |d, e| d[:x] <=> e[:x] }
|
56
|
+
|
57
|
+
@data_params = Hash.new
|
58
|
+
# default X values, if data is not empty
|
59
|
+
if @data.size > 0
|
60
|
+
@data_params[:x_min] = @data.first[:x] || @options[:default_x_min]
|
61
|
+
@data_params[:x_max] = @data.last[:x] || @options[:default_x_max]
|
62
|
+
|
63
|
+
# default Y values
|
64
|
+
y_sort = @data.sort { |a, b| a[:y] <=> b[:y] }
|
65
|
+
@data_params[:y_min] = y_sort.first[:y] || @options[:default_y_min]
|
66
|
+
@data_params[:y_max] = y_sort.last[:y] || @options[:@default_y_max]
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
def x_min
|
72
|
+
@data_params[:x_min]
|
73
|
+
end
|
74
|
+
|
75
|
+
def x_max
|
76
|
+
@data_params[:x_max]
|
77
|
+
end
|
78
|
+
|
79
|
+
def y_min
|
80
|
+
@data_params[:y_min]
|
81
|
+
end
|
82
|
+
|
83
|
+
def y_max
|
84
|
+
@data_params[:y_max]
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
@@ -0,0 +1,248 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
|
3
|
+
# Calculate axis (only) and draw them
|
4
|
+
|
5
|
+
class GraphAxis
|
6
|
+
|
7
|
+
attr_reader :technical_graph
|
8
|
+
|
9
|
+
# Accessor for options Hash
|
10
|
+
def options
|
11
|
+
@technical_graph.options
|
12
|
+
end
|
13
|
+
|
14
|
+
# Accessor for DataLayer Array
|
15
|
+
def layers
|
16
|
+
@technical_graph.layers
|
17
|
+
end
|
18
|
+
|
19
|
+
# Calculate everything
|
20
|
+
def data_processor
|
21
|
+
@technical_graph.data_processor
|
22
|
+
end
|
23
|
+
|
24
|
+
def image_drawer
|
25
|
+
@technical_graph.image_drawer
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(technical_graph)
|
29
|
+
@technical_graph = technical_graph
|
30
|
+
end
|
31
|
+
|
32
|
+
def x_axis_fixed?
|
33
|
+
options[:x_axises_fixed_interval] == true
|
34
|
+
end
|
35
|
+
|
36
|
+
# Value axis has fixed count
|
37
|
+
def y_axis_fixed?
|
38
|
+
options[:y_axises_fixed_interval] == true
|
39
|
+
end
|
40
|
+
|
41
|
+
# Where to put axis values
|
42
|
+
def value_axises
|
43
|
+
return calc_axis(data_processor.y_min, data_processor.y_max, options[:y_axises_interval], options[:y_axises_count], y_axis_fixed?)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Where to put axis values
|
47
|
+
def parameter_axises
|
48
|
+
return calc_axis(data_processor.x_min, data_processor.x_max, options[:x_axises_interval], options[:x_axises_count], x_axis_fixed?)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Calculate axis using 2 methods
|
52
|
+
def calc_axis(from, to, interval, count, fixed_interval)
|
53
|
+
axises = Array.new
|
54
|
+
l = to - from
|
55
|
+
current = from
|
56
|
+
|
57
|
+
if fixed_interval
|
58
|
+
while current < to
|
59
|
+
axises << current
|
60
|
+
current += interval
|
61
|
+
end
|
62
|
+
return axises
|
63
|
+
|
64
|
+
else
|
65
|
+
(0...count).each do |i|
|
66
|
+
axises << from + (l.to_f * i.to_f) / count.to_f
|
67
|
+
end
|
68
|
+
return axises
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
# Render axis on image
|
75
|
+
def render_on_image(image)
|
76
|
+
@image = image
|
77
|
+
|
78
|
+
render_values_axis
|
79
|
+
render_parameters_axis
|
80
|
+
|
81
|
+
render_values_zero_axis
|
82
|
+
render_parameters_zero_axis
|
83
|
+
end
|
84
|
+
|
85
|
+
def axis_antialias
|
86
|
+
options[:axis_antialias] == true
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
def render_values_axis
|
91
|
+
plot_axis_y_line = Magick::Draw.new
|
92
|
+
plot_axis_y_text = Magick::Draw.new
|
93
|
+
|
94
|
+
plot_axis_y_line.stroke_antialias(axis_antialias)
|
95
|
+
plot_axis_y_text.text_antialias(image_drawer.font_antialias)
|
96
|
+
|
97
|
+
plot_axis_y_line.fill_opacity(0)
|
98
|
+
plot_axis_y_line.stroke(options[:axis_color])
|
99
|
+
plot_axis_y_line.stroke_opacity(1.0)
|
100
|
+
plot_axis_y_line.stroke_width(1.0)
|
101
|
+
plot_axis_y_line.stroke_linecap('square')
|
102
|
+
plot_axis_y_line.stroke_linejoin('miter')
|
103
|
+
|
104
|
+
plot_axis_y_text.font_family('helvetica')
|
105
|
+
plot_axis_y_text.font_style(Magick::NormalStyle)
|
106
|
+
plot_axis_y_text.text_align(Magick::LeftAlign)
|
107
|
+
plot_axis_y_text.text_undercolor(options[:background_color])
|
108
|
+
|
109
|
+
value_axises.each do |y|
|
110
|
+
by = image_drawer.calc_bitmap_y(y)
|
111
|
+
plot_axis_y_line.line(
|
112
|
+
0, by.round,
|
113
|
+
@image.columns-1, by.round
|
114
|
+
)
|
115
|
+
|
116
|
+
plot_axis_y_text.text(
|
117
|
+
5,
|
118
|
+
by.round + 15,
|
119
|
+
"#{y}"
|
120
|
+
)
|
121
|
+
end
|
122
|
+
|
123
|
+
t = Time.now
|
124
|
+
plot_axis_y_line.draw(@image)
|
125
|
+
puts "#{Time.now - t} drawing lines"
|
126
|
+
plot_axis_y_text.draw(@image)
|
127
|
+
puts "#{Time.now - t} drawing text"
|
128
|
+
end
|
129
|
+
|
130
|
+
def render_parameters_axis
|
131
|
+
|
132
|
+
plot_axis_x_line = Magick::Draw.new
|
133
|
+
plot_axis_x_text = Magick::Draw.new
|
134
|
+
|
135
|
+
plot_axis_x_line.stroke_antialias(axis_antialias)
|
136
|
+
plot_axis_x_text.text_antialias(axis_antialias)
|
137
|
+
|
138
|
+
plot_axis_x_line.fill_opacity(0)
|
139
|
+
plot_axis_x_line.stroke(options[:axis_color])
|
140
|
+
plot_axis_x_line.stroke_opacity(1.0)
|
141
|
+
plot_axis_x_line.stroke_width(1.0)
|
142
|
+
plot_axis_x_line.stroke_linecap('square')
|
143
|
+
plot_axis_x_line.stroke_linejoin('miter')
|
144
|
+
|
145
|
+
plot_axis_x_text.font_family('helvetica')
|
146
|
+
plot_axis_x_text.font_style(Magick::NormalStyle)
|
147
|
+
plot_axis_x_text.text_align(Magick::LeftAlign)
|
148
|
+
plot_axis_x_text.text_undercolor(options[:background_color])
|
149
|
+
|
150
|
+
parameter_axises.each do |x|
|
151
|
+
bx = image_drawer.calc_bitmap_x(x)
|
152
|
+
plot_axis_x_line.line(
|
153
|
+
bx.round, 0,
|
154
|
+
bx.round, @image.rows-1
|
155
|
+
)
|
156
|
+
|
157
|
+
plot_axis_x_text.text(
|
158
|
+
bx.round + 15,
|
159
|
+
@image.rows - 15,
|
160
|
+
"#{x}"
|
161
|
+
)
|
162
|
+
end
|
163
|
+
|
164
|
+
t = Time.now
|
165
|
+
plot_axis_x_line.draw(@image)
|
166
|
+
puts "#{Time.now - t} drawing lines"
|
167
|
+
plot_axis_x_text.draw(@image)
|
168
|
+
puts "#{Time.now - t} drawing text"
|
169
|
+
|
170
|
+
end
|
171
|
+
|
172
|
+
# TODO: make it DRY
|
173
|
+
def render_values_zero_axis
|
174
|
+
plot_axis_y_line = Magick::Draw.new
|
175
|
+
plot_axis_y_text = Magick::Draw.new
|
176
|
+
|
177
|
+
plot_axis_y_line.stroke_antialias(axis_antialias)
|
178
|
+
plot_axis_y_text.text_antialias(image_drawer.font_antialias)
|
179
|
+
|
180
|
+
plot_axis_y_line.fill_opacity(0)
|
181
|
+
plot_axis_y_line.stroke(options[:axis_color])
|
182
|
+
plot_axis_y_line.stroke_opacity(1.0)
|
183
|
+
plot_axis_y_line.stroke_width(2.0)
|
184
|
+
plot_axis_y_line.stroke_linecap('square')
|
185
|
+
plot_axis_y_line.stroke_linejoin('miter')
|
186
|
+
|
187
|
+
plot_axis_y_text.font_family('helvetica')
|
188
|
+
plot_axis_y_text.font_style(Magick::NormalStyle)
|
189
|
+
plot_axis_y_text.text_align(Magick::LeftAlign)
|
190
|
+
plot_axis_y_text.text_undercolor(options[:background_color])
|
191
|
+
|
192
|
+
y = 0.0
|
193
|
+
by = image_drawer.calc_bitmap_y(y)
|
194
|
+
plot_axis_y_line.line(
|
195
|
+
0, by.round,
|
196
|
+
@image.columns-1, by.round
|
197
|
+
)
|
198
|
+
|
199
|
+
plot_axis_y_text.text(
|
200
|
+
5,
|
201
|
+
by.round + 15,
|
202
|
+
"#{y}"
|
203
|
+
)
|
204
|
+
|
205
|
+
# TODO: why normal axis does not need it?
|
206
|
+
plot_axis_y_line.draw(@image)
|
207
|
+
plot_axis_y_text.draw(@image)
|
208
|
+
end
|
209
|
+
|
210
|
+
def render_parameters_zero_axis
|
211
|
+
|
212
|
+
plot_axis_x_line = Magick::Draw.new
|
213
|
+
plot_axis_x_text = Magick::Draw.new
|
214
|
+
|
215
|
+
plot_axis_x_line.stroke_antialias(axis_antialias)
|
216
|
+
plot_axis_x_text.text_antialias(axis_antialias)
|
217
|
+
|
218
|
+
plot_axis_x_line.fill_opacity(0)
|
219
|
+
plot_axis_x_line.stroke(options[:axis_color])
|
220
|
+
plot_axis_x_line.stroke_opacity(1.0)
|
221
|
+
plot_axis_x_line.stroke_width(2.0)
|
222
|
+
plot_axis_x_line.stroke_linecap('square')
|
223
|
+
plot_axis_x_line.stroke_linejoin('miter')
|
224
|
+
|
225
|
+
plot_axis_x_text.font_family('helvetica')
|
226
|
+
plot_axis_x_text.font_style(Magick::NormalStyle)
|
227
|
+
plot_axis_x_text.text_align(Magick::LeftAlign)
|
228
|
+
plot_axis_x_text.text_undercolor(options[:background_color])
|
229
|
+
|
230
|
+
x = 0.0
|
231
|
+
bx = image_drawer.calc_bitmap_x(x)
|
232
|
+
plot_axis_x_line.line(
|
233
|
+
bx.round, 0,
|
234
|
+
bx.round, @image.rows-1
|
235
|
+
)
|
236
|
+
|
237
|
+
plot_axis_x_text.text(
|
238
|
+
bx.round + 15,
|
239
|
+
@image.rows - 15,
|
240
|
+
"#{x}"
|
241
|
+
)
|
242
|
+
|
243
|
+
# TODO: why normal axis does not need it?
|
244
|
+
plot_axis_x_line.draw(@image)
|
245
|
+
plot_axis_x_text.draw(@image)
|
246
|
+
end
|
247
|
+
|
248
|
+
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
|
3
|
+
require 'technical_graph/data_layer'
|
4
|
+
|
5
|
+
# Calculate every aspect of graph, but not directly image oriented variables
|
6
|
+
|
7
|
+
class GraphDataProcessor
|
8
|
+
attr_reader :technical_graph
|
9
|
+
|
10
|
+
# Accessor for options Hash
|
11
|
+
def options
|
12
|
+
@technical_graph.options
|
13
|
+
end
|
14
|
+
|
15
|
+
# Accessor for DataLayer Array
|
16
|
+
def layers
|
17
|
+
@technical_graph.layers
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(technical_graph)
|
21
|
+
@technical_graph = technical_graph
|
22
|
+
|
23
|
+
options[:x_min] ||= (Time.now - 24 * 3600).to_f
|
24
|
+
options[:x_max] ||= Time.now.to_f
|
25
|
+
options[:y_min] ||= 0.0
|
26
|
+
options[:y_max] ||= 1.0
|
27
|
+
# :default - coords are default
|
28
|
+
# :fixed or whatever else - min/max coords are fixed
|
29
|
+
options[:xy_behaviour] ||= :default
|
30
|
+
|
31
|
+
# number of axises
|
32
|
+
options[:y_axises_count] ||= 10
|
33
|
+
options[:x_axises_count] ||= 10
|
34
|
+
# interval
|
35
|
+
options[:y_axises_interval] ||= 1.0
|
36
|
+
options[:x_axises_interval] ||= 1.0
|
37
|
+
# when false then axises are generated to meet 'count'
|
38
|
+
# when true then axises are generated every X from lowest
|
39
|
+
options[:x_axises_fixed_interval] = true if options[:x_axises_fixed_interval].nil?
|
40
|
+
options[:y_axises_fixed_interval] = true if options[:y_axises_fixed_interval].nil?
|
41
|
+
|
42
|
+
# default truncate string used for rendering numbers
|
43
|
+
options[:truncate_string] ||= "%.2f"
|
44
|
+
|
45
|
+
@zoom_x = 1.0
|
46
|
+
@zoom_y = 1.0
|
47
|
+
end
|
48
|
+
|
49
|
+
# Ranges are fixed
|
50
|
+
def fixed?
|
51
|
+
options[:xy_behaviour] == :fixed
|
52
|
+
end
|
53
|
+
|
54
|
+
# Ranges without zoom
|
55
|
+
def raw_x_min
|
56
|
+
options[:x_min]
|
57
|
+
end
|
58
|
+
|
59
|
+
def raw_x_max
|
60
|
+
options[:x_max]
|
61
|
+
end
|
62
|
+
|
63
|
+
def raw_y_min
|
64
|
+
options[:y_min]
|
65
|
+
end
|
66
|
+
|
67
|
+
def raw_y_max
|
68
|
+
options[:y_max]
|
69
|
+
end
|
70
|
+
|
71
|
+
# Ranges with zoom
|
72
|
+
def x_min
|
73
|
+
calc_x_zoomed([self.raw_x_min]).first
|
74
|
+
end
|
75
|
+
|
76
|
+
def x_max
|
77
|
+
calc_x_zoomed([self.raw_x_max]).first
|
78
|
+
end
|
79
|
+
|
80
|
+
def y_min
|
81
|
+
calc_y_zoomed([self.raw_y_min]).first
|
82
|
+
end
|
83
|
+
|
84
|
+
def y_max
|
85
|
+
calc_y_zoomed([self.raw_y_max]).first
|
86
|
+
end
|
87
|
+
|
88
|
+
# Accessors
|
89
|
+
private
|
90
|
+
def raw_x_min=(x)
|
91
|
+
options[:x_min] = x
|
92
|
+
end
|
93
|
+
|
94
|
+
def raw_x_max=(x)
|
95
|
+
options[:x_max] = x
|
96
|
+
end
|
97
|
+
|
98
|
+
def raw_y_min=(y)
|
99
|
+
options[:y_min] = y
|
100
|
+
end
|
101
|
+
|
102
|
+
def raw_y_max=(y)
|
103
|
+
options[:y_max] = y
|
104
|
+
end
|
105
|
+
|
106
|
+
public
|
107
|
+
|
108
|
+
# Consider changing ranges if needed
|
109
|
+
def process_data_layer(data_layer)
|
110
|
+
# ranges are set, can't change
|
111
|
+
return if fixed?
|
112
|
+
|
113
|
+
# updating ranges
|
114
|
+
self.raw_y_max = data_layer.y_max if not data_layer.y_max.nil? and data_layer.y_max > self.raw_y_max
|
115
|
+
self.raw_x_max = data_layer.x_max if not data_layer.x_max.nil? and data_layer.x_max > self.raw_x_max
|
116
|
+
|
117
|
+
self.raw_y_min = data_layer.y_min if not data_layer.y_min.nil? and data_layer.y_min < self.raw_y_min
|
118
|
+
self.raw_x_min = data_layer.x_min if not data_layer.x_min.nil? and data_layer.x_min < self.raw_x_min
|
119
|
+
end
|
120
|
+
|
121
|
+
# Change overall image zoom
|
122
|
+
def zoom=(z = 1.0)
|
123
|
+
self.x_zoom = z
|
124
|
+
self.y_zoom = z
|
125
|
+
end
|
126
|
+
|
127
|
+
# Change X axis zoom
|
128
|
+
def x_zoom=(z = 1.0)
|
129
|
+
@zoom_x = z
|
130
|
+
end
|
131
|
+
|
132
|
+
# Change X axis zoom
|
133
|
+
def y_zoom=(z = 1.0)
|
134
|
+
@zoom_y = z
|
135
|
+
end
|
136
|
+
|
137
|
+
attr_reader :zoom_x, :zoom_y
|
138
|
+
|
139
|
+
# Calculate zoomed X position for Array of X'es
|
140
|
+
def calc_x_zoomed(old_xes)
|
141
|
+
# default zoom
|
142
|
+
if self.zoom_x == 1.0
|
143
|
+
return old_xes
|
144
|
+
end
|
145
|
+
|
146
|
+
a = (raw_x_max.to_f + raw_x_min.to_f) / 2.0
|
147
|
+
new_xes = Array.new
|
148
|
+
|
149
|
+
old_xes.each do |x|
|
150
|
+
d = x - a
|
151
|
+
new_xes << (a + d * self.zoom_x)
|
152
|
+
end
|
153
|
+
|
154
|
+
return new_xes
|
155
|
+
end
|
156
|
+
|
157
|
+
# Calculate zoomed Y position for Array of Y'es
|
158
|
+
def calc_y_zoomed(old_yes)
|
159
|
+
# default zoom
|
160
|
+
if self.zoom_y == 1.0
|
161
|
+
return old_yes
|
162
|
+
end
|
163
|
+
|
164
|
+
a = (raw_y_max.to_f + raw_y_min.to_f) / 2.0
|
165
|
+
new_yes = Array.new
|
166
|
+
|
167
|
+
old_yes.each do |y|
|
168
|
+
d = y - a
|
169
|
+
new_yes << (a + d * self.zoom_y)
|
170
|
+
end
|
171
|
+
|
172
|
+
return new_yes
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|