grada 2.2.2 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/grada.rb +1 -295
- data/lib/grada/graph.rb +200 -0
- data/lib/grada/types/default.rb +62 -0
- data/lib/grada/types/default_base.rb +123 -0
- data/lib/grada/types/gnuplot.rb +246 -0
- data/lib/grada/types/heat_map.rb +25 -0
- data/lib/grada/types/histogram.rb +48 -0
- metadata +8 -3
- data/lib/grada/gnuplot.rb +0 -244
data/lib/grada.rb
CHANGED
@@ -1,297 +1,3 @@
|
|
1
1
|
$:.unshift File.expand_path('..', __FILE__)
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
class Grada
|
6
|
-
# Not valid the format of the object to construct the graph
|
7
|
-
#
|
8
|
-
class NotValidArrayError < RuntimeError; end
|
9
|
-
|
10
|
-
# Not valid the content of the array you're passing to build the graph
|
11
|
-
#
|
12
|
-
class NotValidDataError < RuntimeError; end
|
13
|
-
|
14
|
-
# Can't build the plot
|
15
|
-
#
|
16
|
-
class NoPlotDataError < RuntimeError; end
|
17
|
-
|
18
|
-
attr_reader :x
|
19
|
-
attr_reader :y
|
20
|
-
|
21
|
-
DEFAULT_OPTIONS = {width: 1920,
|
22
|
-
height: 1080,
|
23
|
-
title: "Graph",
|
24
|
-
x_label: "X",
|
25
|
-
y_label: "Y",
|
26
|
-
with: 'lines',
|
27
|
-
graph_type: :default}
|
28
|
-
|
29
|
-
#All styles you can specify for the plots
|
30
|
-
#
|
31
|
-
STYLES = [:linestyle, :linetype, :linewidth, :linecolor, :pointtype, :pointsize, :fill]
|
32
|
-
|
33
|
-
#Graph offsets
|
34
|
-
#
|
35
|
-
LEFT = 0.05
|
36
|
-
RIGHT = 0.05
|
37
|
-
TOP = 0.05
|
38
|
-
BOTTOM = 0.05
|
39
|
-
|
40
|
-
# Hello GraDA
|
41
|
-
#
|
42
|
-
|
43
|
-
def self.hi
|
44
|
-
puts "Hello GraDA"
|
45
|
-
end
|
46
|
-
|
47
|
-
# Initialize object with the data you want to plot.
|
48
|
-
# It can vary depending on the type of graph.
|
49
|
-
# The second argument is optional.
|
50
|
-
#
|
51
|
-
# Example:
|
52
|
-
# >> radiation_levels_median_per_day = [0.001,0.01,1,10,1000]
|
53
|
-
# >> radiation_days = [0,1,2,3,4]
|
54
|
-
# >> grada = Grada.new(radiation_days, radiation_levels_median_per_day)
|
55
|
-
# => #<Grada:0x007f962a8dc9b8 @x=[0, 1, 2, 3, 4], @y=[0.001, 0.01, 1, 10, 1000]>
|
56
|
-
# Arguments:
|
57
|
-
# x: (Array)
|
58
|
-
# y: (Array) *optional*
|
59
|
-
|
60
|
-
def initialize(x, y = nil)
|
61
|
-
@x = validate(x)
|
62
|
-
@y = y.nil? ? y : validate(y)
|
63
|
-
end
|
64
|
-
|
65
|
-
# Displays a graph in a X11 window.
|
66
|
-
# You can specify all the options that you need:
|
67
|
-
# *width* (Integer)
|
68
|
-
# *height* (Integer)
|
69
|
-
# *title* (Integer)
|
70
|
-
# *x_label* (String)
|
71
|
-
# *y_label* (String)
|
72
|
-
# *graph_type* (:histogram, :heatmap) default: :default
|
73
|
-
# *with* ('points', 'linespoints') default: 'lines'
|
74
|
-
#
|
75
|
-
# Also is important to know that you can interact with the graph:
|
76
|
-
# * Zoom in => right click and drag the mouse to cover the area you want
|
77
|
-
# or
|
78
|
-
# use the scroll wheel
|
79
|
-
#
|
80
|
-
# * Zoom out => press key 'a'
|
81
|
-
# or
|
82
|
-
# if you want to go back to a previous state of zoom press key 'p'
|
83
|
-
#
|
84
|
-
# * Exit interactive mode => press key 'q'
|
85
|
-
# or
|
86
|
-
# just close the window
|
87
|
-
#
|
88
|
-
# * Save image => working on it
|
89
|
-
#
|
90
|
-
# Example:
|
91
|
-
# >> grada.display
|
92
|
-
# => ""
|
93
|
-
# >> grada.display({ title: 'Atomic Device X', x_label: 'Day', y_label: 'smSv', with: 'points' })
|
94
|
-
# => ""
|
95
|
-
# Arguments:
|
96
|
-
# opts: (Hash) *optional*
|
97
|
-
|
98
|
-
def display(opts = {})
|
99
|
-
@opts = DEFAULT_OPTIONS.merge(opts)
|
100
|
-
|
101
|
-
if @opts[:graph_type] == :histogram
|
102
|
-
population_data?(@x)
|
103
|
-
return nil if @x.empty?
|
104
|
-
|
105
|
-
plot_histogram do |plot|
|
106
|
-
plot.set "terminal x11 size #{@opts[:width]},#{@opts[:height]}"
|
107
|
-
plot.set "offset graph #{LEFT},#{RIGHT},#{TOP},#{BOTTOM}"
|
108
|
-
end
|
109
|
-
elsif @opts[:graph_type] == :heatmap
|
110
|
-
Matrix.columns(@x) rescue raise NoPlotDataError
|
111
|
-
@opts[:with] = 'image'
|
112
|
-
|
113
|
-
plot_heat_map do |plot|
|
114
|
-
plot.set "terminal x11 size #{@opts[:width]},#{@opts[:height]}"
|
115
|
-
plot.set "offset graph #{LEFT},#{RIGHT},#{TOP},#{BOTTOM}"
|
116
|
-
end
|
117
|
-
else
|
118
|
-
raise NoPlotDataError if @y.nil?
|
119
|
-
|
120
|
-
plot_and do |plot|
|
121
|
-
plot.set "terminal x11 size #{@opts[:width]},#{@opts[:height]}"
|
122
|
-
plot.set "offset graph #{LEFT},#{RIGHT},#{TOP},#{BOTTOM}"
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
# Save the graph in a png file.
|
128
|
-
# You can specify all the options that you need as _display_ but also need to specify the file root-name and extension.
|
129
|
-
# The possible extensions you can use for saving a file are:
|
130
|
-
# *png*
|
131
|
-
# *gif*
|
132
|
-
# *jpeg*
|
133
|
-
# *svg* => default
|
134
|
-
#
|
135
|
-
# Example:
|
136
|
-
# >> grada.save({ filename: 'secret/radiation_levels/ffa/zonex/devicex/radiation_level_malaga', ext: 'png' ,title: 'Atomic Device X', x_label: 'Day', y_label: 'smSv', with: 'points' })
|
137
|
-
# => ""
|
138
|
-
# Arguments:
|
139
|
-
# opts: (Hash) *optional*
|
140
|
-
|
141
|
-
def save(opts = {})
|
142
|
-
@opts = DEFAULT_OPTIONS.merge(opts)
|
143
|
-
|
144
|
-
return nil if @opts[:filename].nil?
|
145
|
-
|
146
|
-
ext = @opts[:ext] || 'svg'
|
147
|
-
|
148
|
-
if @opts[:graph_type] == :histogram
|
149
|
-
population_data?(@x)
|
150
|
-
return nil if @x.empty?
|
151
|
-
|
152
|
-
plot_histogram do |plot|
|
153
|
-
plot.output "#{@opts[:filename]}.#{ext}"
|
154
|
-
plot.set "terminal #{ext} size #{@opts[:width]}, #{@opts[:height]} crop"
|
155
|
-
plot.set "offset graph #{LEFT},#{RIGHT},#{TOP},#{BOTTOM}"
|
156
|
-
end
|
157
|
-
elsif @opts[:graph_type] == :heatmap
|
158
|
-
Matrix.columns(@x) rescue raise NoPlotDataError
|
159
|
-
@opts[:with] = 'image'
|
160
|
-
|
161
|
-
plot_heat_map do |plot|
|
162
|
-
plot.output "#{@opts[:filename]}.#{ext}"
|
163
|
-
plot.set "terminal #{ext} size #{@opts[:width]}, #{@opts[:height]} crop"
|
164
|
-
end
|
165
|
-
else
|
166
|
-
raise NoPlotDataError if @y.nil?
|
167
|
-
|
168
|
-
plot_and do |plot|
|
169
|
-
plot.output "#{@opts[:filename]}.#{ext}"
|
170
|
-
plot.set "terminal #{ext} size #{@opts[:width]}, #{@opts[:height]} crop"
|
171
|
-
plot.set "offset graph #{LEFT},#{RIGHT},#{TOP},#{BOTTOM}"
|
172
|
-
end
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
|
-
private
|
177
|
-
|
178
|
-
def validate(l)
|
179
|
-
raise NotValidArrayError if ! l.is_a?(Array)
|
180
|
-
|
181
|
-
l.each do |elem|
|
182
|
-
raise NotValidDataError if ! ( elem.is_a?(Float) || elem.is_a?(Integer) || elem.is_a?(Array) || elem.is_a?(Hash))
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
def population_data?(l)
|
187
|
-
raise NotValidArrayError if ! l.is_a?(Array)
|
188
|
-
|
189
|
-
l.each do |elem|
|
190
|
-
raise NotValidDataError if ! ( elem.is_a?(Float) || elem.is_a?(Integer))
|
191
|
-
end
|
192
|
-
end
|
193
|
-
|
194
|
-
def multiple_data?(l)
|
195
|
-
if l.is_a?(Array)
|
196
|
-
l.each do |elem|
|
197
|
-
return false if ! elem.is_a?(Hash)
|
198
|
-
end
|
199
|
-
|
200
|
-
return true
|
201
|
-
end
|
202
|
-
|
203
|
-
false
|
204
|
-
end
|
205
|
-
|
206
|
-
def plot_and(&block)
|
207
|
-
Gnuplot.open do
|
208
|
-
Gnuplot::Plot.construct do |plot|
|
209
|
-
block.call plot if block
|
210
|
-
|
211
|
-
plot.title @opts[:title]
|
212
|
-
|
213
|
-
plot.xlabel @opts[:x_label]
|
214
|
-
plot.ylabel @opts[:y_label]
|
215
|
-
|
216
|
-
if multiple_data?(@y)
|
217
|
-
@y.each_with_index do |dic, index|
|
218
|
-
dic.each do |k, v|
|
219
|
-
if ! STYLES.include?(k.to_sym) && k.to_sym != :with
|
220
|
-
raise NoPlotDataError if ! v.nil? && @x.size != v.size
|
221
|
-
|
222
|
-
style = Gnuplot::Style.new do |ds|
|
223
|
-
ds.index = index
|
224
|
-
STYLES.each do |style|
|
225
|
-
ds.send("#{style}=", dic[style]) if dic[style]
|
226
|
-
end
|
227
|
-
end.to_s
|
228
|
-
|
229
|
-
plot.data << Gnuplot::DataSet.new([@x,v]) do |ds|
|
230
|
-
ds.with = dic[:with] || @opts[:with]
|
231
|
-
ds.with += style
|
232
|
-
ds.title = "#{k}"
|
233
|
-
end
|
234
|
-
end
|
235
|
-
end
|
236
|
-
end
|
237
|
-
else
|
238
|
-
raise NoPlotDataError if ! @y.nil? && @x.size != @y.size
|
239
|
-
|
240
|
-
plot.data << Gnuplot::DataSet.new([@x,@y]) do |ds|
|
241
|
-
ds.with = @opts[:with]
|
242
|
-
end
|
243
|
-
end
|
244
|
-
end
|
245
|
-
end
|
246
|
-
end
|
247
|
-
|
248
|
-
def plot_histogram(&block)
|
249
|
-
Gnuplot.open do
|
250
|
-
Gnuplot::Plot.construct do |plot|
|
251
|
-
block.call plot if block
|
252
|
-
|
253
|
-
width = ( @x.max - @x.min ) / @x.size
|
254
|
-
|
255
|
-
plot.title @opts[:title]
|
256
|
-
|
257
|
-
plot.set "style data histogram"
|
258
|
-
plot.xlabel @opts[:x_label]
|
259
|
-
plot.ylabel "Frequency"
|
260
|
-
plot.set "style fill solid 0.5"
|
261
|
-
plot.set "xrange [#{@x.min}:#{@x.max}]"
|
262
|
-
plot.set "boxwidth #{ width * 0.1}"
|
263
|
-
plot.set "xtics #{@x.min},#{(@x.max-@x.min)/5},#{@x.max}"
|
264
|
-
plot.set "tics out nomirror"
|
265
|
-
|
266
|
-
plot.data << Gnuplot::DataSet.new(@x) do |ds|
|
267
|
-
ds.with = 'boxes'
|
268
|
-
ds.title = @opts[:x_label]
|
269
|
-
ds.using = '($1):(1.0)'
|
270
|
-
ds.smooth = 'freq'
|
271
|
-
end
|
272
|
-
end
|
273
|
-
end
|
274
|
-
end
|
275
|
-
|
276
|
-
def plot_heat_map(&block)
|
277
|
-
Gnuplot.open do
|
278
|
-
Gnuplot::Plot.construct do |plot|
|
279
|
-
block.call plot if block
|
280
|
-
|
281
|
-
plot.set "pm3d map"
|
282
|
-
plot.set "palette color"
|
283
|
-
plot.set "xrange [0:#{@x.size-1}]"
|
284
|
-
plot.set "yrange [0:#{@x.size-1}]"
|
285
|
-
plot.set "cbrange [#{@opts[:min]}:#{@opts[:max]}]"
|
286
|
-
plot.set "cblabel \"#{@opts[:x_label]}\""
|
287
|
-
plot.set "palette model RGB"
|
288
|
-
plot.set "palette define"
|
289
|
-
|
290
|
-
plot.title @opts[:title]
|
291
|
-
plot.data << Gnuplot::DataSet.new(Matrix.columns(@x)) do |ds|
|
292
|
-
ds.with = @opts[:with]
|
293
|
-
end
|
294
|
-
end
|
295
|
-
end
|
296
|
-
end
|
297
|
-
end
|
3
|
+
require 'grada/graph'
|
data/lib/grada/graph.rb
ADDED
@@ -0,0 +1,200 @@
|
|
1
|
+
require 'grada/types/default_base'
|
2
|
+
require 'grada/types/gnuplot'
|
3
|
+
require 'grada/types/histogram'
|
4
|
+
require 'grada/types/default'
|
5
|
+
require 'grada/types/heat_map'
|
6
|
+
|
7
|
+
module Grada
|
8
|
+
class Graph
|
9
|
+
# Not valid the format of the object to construct the graph
|
10
|
+
#
|
11
|
+
class NotValidArrayError < RuntimeError; end
|
12
|
+
|
13
|
+
# Not valid the content of the array you're passing to build the graph
|
14
|
+
#
|
15
|
+
class NotValidDataError < RuntimeError; end
|
16
|
+
|
17
|
+
# Can't build the plot
|
18
|
+
#
|
19
|
+
class NoPlotDataError < RuntimeError; end
|
20
|
+
|
21
|
+
attr_reader :x
|
22
|
+
attr_reader :y
|
23
|
+
|
24
|
+
DEFAULT_OPTIONS = {width: 1920,
|
25
|
+
height: 1080,
|
26
|
+
title: "Graph",
|
27
|
+
x_label: "X",
|
28
|
+
y_label: "Y",
|
29
|
+
with: 'lines',
|
30
|
+
graph_type: :default}
|
31
|
+
|
32
|
+
#Graph offsets
|
33
|
+
#
|
34
|
+
LEFT = 0.05
|
35
|
+
RIGHT = 0.05
|
36
|
+
TOP = 0.05
|
37
|
+
BOTTOM = 0.05
|
38
|
+
|
39
|
+
# Hello GraDA
|
40
|
+
#
|
41
|
+
|
42
|
+
def self.hi
|
43
|
+
puts "Hello GraDA"
|
44
|
+
end
|
45
|
+
|
46
|
+
# Initialize object with the data you want to plot.
|
47
|
+
# It can vary depending on the type of graph.
|
48
|
+
# The second argument is optional.
|
49
|
+
#
|
50
|
+
# Example:
|
51
|
+
# >> radiation_levels_median_per_day = [0.001,0.01,1,10,1000]
|
52
|
+
# >> radiation_days = [0,1,2,3,4]
|
53
|
+
# >> grada = Grada.new(radiation_days, radiation_levels_median_per_day)
|
54
|
+
# => #<Grada:0x007f962a8dc9b8 @x=[0, 1, 2, 3, 4], @y=[0.001, 0.01, 1, 10, 1000]>
|
55
|
+
# Arguments:
|
56
|
+
# x: (Array)
|
57
|
+
# y: (Array) *optional*
|
58
|
+
|
59
|
+
def initialize(x, y = nil)
|
60
|
+
@x = validate(x)
|
61
|
+
@y = y.nil? ? y : validate(y)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Displays a graph in a X11 window.
|
65
|
+
# You can specify all the options that you need:
|
66
|
+
# *width* (Integer)
|
67
|
+
# *height* (Integer)
|
68
|
+
# *title* (Integer)
|
69
|
+
# *x_label* (String)
|
70
|
+
# *y_label* (String)
|
71
|
+
# *graph_type* (:histogram, :heatmap) default: :default
|
72
|
+
# *with* ('points', 'linespoints') default: 'lines'
|
73
|
+
#
|
74
|
+
# Also is important to know that you can interact with the graph:
|
75
|
+
# * Zoom in => right click and drag the mouse to cover the area you want
|
76
|
+
# or
|
77
|
+
# use the scroll wheel
|
78
|
+
#
|
79
|
+
# * Zoom out => press key 'a'
|
80
|
+
# or
|
81
|
+
# if you want to go back to a previous state of zoom press key 'p'
|
82
|
+
#
|
83
|
+
# * Exit interactive mode => press key 'q'
|
84
|
+
# or
|
85
|
+
# just close the window
|
86
|
+
#
|
87
|
+
# * Save image => working on it
|
88
|
+
#
|
89
|
+
# Example:
|
90
|
+
# >> grada.display
|
91
|
+
# => ""
|
92
|
+
# >> grada.display({ title: 'Atomic Device X', x_label: 'Day', y_label: 'smSv', with: 'points' })
|
93
|
+
# => ""
|
94
|
+
# Arguments:
|
95
|
+
# opts: (Hash) *optional*
|
96
|
+
|
97
|
+
def display(opts = {})
|
98
|
+
@opts = DEFAULT_OPTIONS.merge(opts)
|
99
|
+
|
100
|
+
if @opts[:graph_type] == :histogram
|
101
|
+
population_data?(@x)
|
102
|
+
return nil if @x.empty?
|
103
|
+
|
104
|
+
Histogram.plot(@x, @opts) do |plot|
|
105
|
+
plot.set "terminal x11 size #{@opts[:width]},#{@opts[:height]}"
|
106
|
+
plot.set "offset graph #{LEFT},#{RIGHT},#{TOP},#{BOTTOM}"
|
107
|
+
end
|
108
|
+
elsif @opts[:graph_type] == :heatmap
|
109
|
+
Matrix.columns(@x) rescue raise NoPlotDataError
|
110
|
+
@opts[:with] = 'image'
|
111
|
+
|
112
|
+
HeatMap.plot(@x, @opts) do |plot|
|
113
|
+
plot.set "terminal x11 size #{@opts[:width]},#{@opts[:height]}"
|
114
|
+
plot.set "offset graph #{LEFT},#{RIGHT},#{TOP},#{BOTTOM}"
|
115
|
+
end
|
116
|
+
else
|
117
|
+
raise NoPlotDataError if @y.nil?
|
118
|
+
|
119
|
+
Default.plot(@x, @y, @opts) do |plot|
|
120
|
+
plot.set "terminal x11 size #{@opts[:width]},#{@opts[:height]}"
|
121
|
+
plot.set "offset graph #{LEFT},#{RIGHT},#{TOP},#{BOTTOM}"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Save the graph in a png file.
|
127
|
+
# You can specify all the options that you need as _display_ but also need to specify the file root-name and extension.
|
128
|
+
# The possible extensions you can use for saving a file are:
|
129
|
+
# *png*
|
130
|
+
# *gif*
|
131
|
+
# *jpeg*
|
132
|
+
# *html* (not valid for heatmaps)
|
133
|
+
# *svg* => default (not valid for heatmaps)
|
134
|
+
#
|
135
|
+
# Example:
|
136
|
+
# >> grada.save({ filename: 'secret/radiation_levels/ffa/zonex/devicex/radiation_level_malaga', ext: 'png' ,title: 'Atomic Device X', x_label: 'Day', y_label: 'smSv', with: 'points' })
|
137
|
+
# => ""
|
138
|
+
# Arguments:
|
139
|
+
# opts: (Hash) *optional*
|
140
|
+
|
141
|
+
def save(opts = {})
|
142
|
+
@opts = DEFAULT_OPTIONS.merge(opts)
|
143
|
+
|
144
|
+
return nil if @opts[:filename].nil?
|
145
|
+
|
146
|
+
ext = @opts[:ext] || 'svg'
|
147
|
+
|
148
|
+
if @opts[:graph_type] == :histogram
|
149
|
+
population_data?(@x)
|
150
|
+
return nil if @x.empty?
|
151
|
+
|
152
|
+
return Histogram.plot_html(@x, @opts) if ext == 'html'
|
153
|
+
|
154
|
+
Histogram.plot(@x, @opts) do |plot|
|
155
|
+
plot.output "#{@opts[:filename]}.#{ext}"
|
156
|
+
plot.set "terminal #{ext} size #{@opts[:width]}, #{@opts[:height]} crop"
|
157
|
+
plot.set "offset graph #{LEFT},#{RIGHT},#{TOP},#{BOTTOM}"
|
158
|
+
end
|
159
|
+
elsif @opts[:graph_type] == :heatmap
|
160
|
+
Matrix.columns(@x) rescue raise NoPlotDataError
|
161
|
+
@opts[:with] = 'image'
|
162
|
+
|
163
|
+
ext = 'png' if ext == 'html' || ext == 'svg'
|
164
|
+
|
165
|
+
HeatMap.plot(@x, @opts) do |plot|
|
166
|
+
plot.output "#{@opts[:filename]}.#{ext}"
|
167
|
+
plot.set "terminal #{ext} size #{@opts[:width]}, #{@opts[:height]} crop"
|
168
|
+
end
|
169
|
+
else
|
170
|
+
raise NoPlotDataError if @y.nil?
|
171
|
+
|
172
|
+
return Default.plot_html(@x, @y, @opts) if ext == 'html'
|
173
|
+
|
174
|
+
Default.plot(@x, @y, @opts) do |plot|
|
175
|
+
plot.output "#{@opts[:filename]}.#{ext}"
|
176
|
+
plot.set "terminal #{ext} size #{@opts[:width]}, #{@opts[:height]} crop"
|
177
|
+
plot.set "offset graph #{LEFT},#{RIGHT},#{TOP},#{BOTTOM}"
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
private
|
183
|
+
|
184
|
+
def validate(l)
|
185
|
+
raise NotValidArrayError if ! l.is_a?(Array)
|
186
|
+
|
187
|
+
l.each do |elem|
|
188
|
+
raise NotValidDataError if ! ( elem.is_a?(Float) || elem.is_a?(Integer) || elem.is_a?(Array) || elem.is_a?(Hash))
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def population_data?(l)
|
193
|
+
raise NotValidArrayError if ! l.is_a?(Array)
|
194
|
+
|
195
|
+
l.each do |elem|
|
196
|
+
raise NotValidDataError if ! ( elem.is_a?(Float) || elem.is_a?(Integer))
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Grada
|
2
|
+
class Default < DefaultBase
|
3
|
+
def self.plot(x, y, opts, &block)
|
4
|
+
Gnuplot.open do
|
5
|
+
Gnuplot::Plot.construct do |plot|
|
6
|
+
block.call plot if block
|
7
|
+
|
8
|
+
plot.title opts[:title]
|
9
|
+
|
10
|
+
plot.xlabel opts[:x_label]
|
11
|
+
plot.ylabel opts[:y_label]
|
12
|
+
|
13
|
+
if multiple_data?(y)
|
14
|
+
y.each_with_index do |dic, index|
|
15
|
+
dic.each do |k, v|
|
16
|
+
if ! Grada::STYLES.include?(k.to_sym) && k.to_sym != :with
|
17
|
+
raise NoPlotDataError if ! v.nil? && x.size != v.size
|
18
|
+
|
19
|
+
style = Gnuplot::Style.new do |ds|
|
20
|
+
ds.index = index
|
21
|
+
Grada::STYLES.each do |style|
|
22
|
+
ds.send("#{style}=", dic[style]) if dic[style]
|
23
|
+
end
|
24
|
+
end.to_s
|
25
|
+
|
26
|
+
plot.data << Gnuplot::DataSet.new([x,v]) do |ds|
|
27
|
+
ds.with = dic[:with] || opts[:with]
|
28
|
+
ds.with += style
|
29
|
+
ds.title = "#{k}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
else
|
35
|
+
raise NoPlotDataError if ! y.nil? && x.size != y.size
|
36
|
+
|
37
|
+
plot.data << Gnuplot::DataSet.new([x,y]) do |ds|
|
38
|
+
ds.with = opts[:with]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.plot_html(x, y, opts)
|
46
|
+
opts[:filename] = create_html_dir(opts[:filename])
|
47
|
+
|
48
|
+
create_grada_json(opts, x, y)
|
49
|
+
|
50
|
+
File.open("#{opts[:filename]}.html",'w') do |f|
|
51
|
+
f << html_head
|
52
|
+
f << "<body>\n"
|
53
|
+
f << " <div class=grada_main>\n"
|
54
|
+
f << html_title(opts[:title])
|
55
|
+
f << html_graph
|
56
|
+
f << html_panel
|
57
|
+
f << " </div>"
|
58
|
+
f << "</body>"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Grada
|
4
|
+
#All styles you can specify for the plots
|
5
|
+
#
|
6
|
+
STYLES = [:linestyle, :linetype, :linewidth, :linecolor, :pointtype, :pointsize, :fill]
|
7
|
+
|
8
|
+
class DefaultBase
|
9
|
+
|
10
|
+
protected
|
11
|
+
|
12
|
+
JQUERY_JS = 'https://rawgithub.com/emfigo/flot/master/jquery.js'
|
13
|
+
FLOT_EXCANVAS_JS = 'https://rawgithub.com/emfigo/flot/master/excanvas.min.js'
|
14
|
+
FLOT_JS = 'https://rawgithub.com/emfigo/flot/master/jquery.flot.js'
|
15
|
+
FLOT_SELECTION_JS = 'https://rawgithub.com/emfigo/flot/master/jquery.flot.selection.js'
|
16
|
+
FLOT_SYMBOL_JS = 'https://rawgithub.com/emfigo/flot/master/jquery.flot.symbol.js'
|
17
|
+
FLOT_THRESHOLD_JS = 'https://rawgithub.com/emfigo/flot/master/jquery.flot.threshold.js'
|
18
|
+
GRADA_JS = 'https://rawgithub.com/emfigo/grada/master/assets/javascripts/grada.js'
|
19
|
+
|
20
|
+
TITLE_CSS = 'https://rawgithub.com/emfigo/grada/master/assets/stylesheets/grada_title.css'
|
21
|
+
BODY_CSS = 'https://rawgithub.com/emfigo/grada/master/assets/stylesheets/grada_body.css'
|
22
|
+
|
23
|
+
def self.multiple_data?(l)
|
24
|
+
if l.is_a?(Array)
|
25
|
+
l.each do |elem|
|
26
|
+
return false if ! elem.is_a?(Hash)
|
27
|
+
end
|
28
|
+
|
29
|
+
return true
|
30
|
+
end
|
31
|
+
|
32
|
+
false
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.create_html_dir(path)
|
36
|
+
path.split("/").tap do |dirs|
|
37
|
+
last_dir = dirs.pop
|
38
|
+
|
39
|
+
dirs << "#{last_dir}_grada_html"
|
40
|
+
FileUtils.mkdir_p(dirs.join("/"))
|
41
|
+
dirs << last_dir
|
42
|
+
end.join('/')
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.create_grada_json(opts, x, y = nil)
|
46
|
+
filename = opts[:filename].split("/").tap do |file|
|
47
|
+
hidden_file = file.pop
|
48
|
+
|
49
|
+
file << ".grada_#{opts[:graph_type]}"
|
50
|
+
end.join("/")
|
51
|
+
|
52
|
+
|
53
|
+
File.open("#{filename}.json",'w') do |f|
|
54
|
+
f << generate_grada_json(opts, x, y)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.html_head
|
59
|
+
"<head>
|
60
|
+
<script src='#{JQUERY_JS}' type='text/javascript'></script>
|
61
|
+
<script src='#{FLOT_EXCANVAS_JS}' type='text/javascript'></script>
|
62
|
+
<script src='#{FLOT_JS}' type='text/javascript'></script>
|
63
|
+
<script src='#{FLOT_SELECTION_JS}' type='text/javascript'></script>
|
64
|
+
<script src='#{FLOT_SYMBOL_JS}' type='text/javascript'></script>
|
65
|
+
<script src='#{FLOT_THRESHOLD_JS}' type='text/javascript'></script>
|
66
|
+
<script src='#{GRADA_JS}' type='text/javascript'></script>
|
67
|
+
|
68
|
+
<link href='#{TITLE_CSS}' rel='stylesheet' type='text/css' />
|
69
|
+
<link href='#{BODY_CSS}' rel='stylesheet' type='text/css' />
|
70
|
+
</head>\n"
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.html_title(title)
|
74
|
+
" <div class='grada_title'>
|
75
|
+
<h2>#{title}</h2>
|
76
|
+
</div>\n"
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.html_graph
|
80
|
+
" <div class='grada_body'>
|
81
|
+
<div id='grada_graph'></div>
|
82
|
+
</div>\n"
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.html_panel
|
86
|
+
" <div class='grada_panel'>
|
87
|
+
<span id='reset'>Reset Zoom</span>
|
88
|
+
</div>\n"
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
def self.compose_grada_json_data(x, y)
|
94
|
+
if y.nil?
|
95
|
+
x.sort.group_by{ |elem| elem }.map{ |k,v| { x: k, y: v.size } }
|
96
|
+
else
|
97
|
+
x.zip(y).map do |comp|
|
98
|
+
{ x: comp.first, y: comp.last }
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.generate_grada_json(opts, x, y = nil)
|
104
|
+
json = []
|
105
|
+
|
106
|
+
if multiple_data?(y)
|
107
|
+
y.each do |dic|
|
108
|
+
dic.each do |k, v|
|
109
|
+
if ! Grada::STYLES.include?(k.to_sym) && k.to_sym != :with
|
110
|
+
raise NoPlotDataError if ! v.nil? && x.size != v.size
|
111
|
+
|
112
|
+
json << { data: compose_grada_json_data(x, v), label: "#{k}", style: dic[:with] || opts[:with] }
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
else
|
117
|
+
json << { data: compose_grada_json_data(x, y), label: opts[:title], style: opts[:with] }
|
118
|
+
end
|
119
|
+
|
120
|
+
JSON.pretty_generate(json)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,246 @@
|
|
1
|
+
require 'matrix'
|
2
|
+
require 'open3'
|
3
|
+
|
4
|
+
module Grada
|
5
|
+
class NoGnuPlotExecutableFound < RuntimeError; end
|
6
|
+
|
7
|
+
class Gnuplot
|
8
|
+
def self.candidate?(candidate)
|
9
|
+
return candidate if File::executable? candidate
|
10
|
+
|
11
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).each do |dir|
|
12
|
+
possible_candidate = File::join dir, candidate.strip
|
13
|
+
|
14
|
+
return possible_candidate if File::executable? possible_candidate
|
15
|
+
end
|
16
|
+
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.find_exec(bin)
|
21
|
+
bin_list = RUBY_PLATFORM =~ /mswin|mingw/ ? [bin, "#{bin}.exe"] : [bin]
|
22
|
+
|
23
|
+
bin_list.each do |c|
|
24
|
+
exec = candidate?(c)
|
25
|
+
return exec if exec
|
26
|
+
end
|
27
|
+
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.gnuplot
|
32
|
+
gnu_exec = find_exec( ENV['RB_GNUPLOT'] || 'gnuplot' )
|
33
|
+
raise Grada::NoGnuPlotExecutableFound unless gnu_exec
|
34
|
+
gnu_exec
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.open(persist = true, &block)
|
38
|
+
gnuplot_cmd = gnuplot
|
39
|
+
|
40
|
+
commands = yield
|
41
|
+
|
42
|
+
output = StringIO.new
|
43
|
+
Open3::popen3(gnuplot_cmd, '-persist') do |data_in, data_out, stderr, wait_th|
|
44
|
+
data_in << commands[:plot_settings]
|
45
|
+
data_in << commands[:plot_data]
|
46
|
+
|
47
|
+
data_in.flush
|
48
|
+
sleep 1
|
49
|
+
|
50
|
+
while true do
|
51
|
+
window = IO::popen('xprop -name "Gnuplot" WM_NAME 2>/dev/null').gets
|
52
|
+
break unless window
|
53
|
+
sleep 1
|
54
|
+
end
|
55
|
+
data_in.close
|
56
|
+
data_out.close
|
57
|
+
stderr.close
|
58
|
+
|
59
|
+
output.write(wait_th.value.exitstatus)
|
60
|
+
end
|
61
|
+
|
62
|
+
output.string
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class Gnuplot::Plot
|
67
|
+
attr_accessor :cmd, :data, :settings, :styles, :arbitrary_lines
|
68
|
+
|
69
|
+
QUOTED_METHODS = [ "title", "output", "xlabel", "x2label", "ylabel", "y2label", "clabel", "cblabel", "zlabel" ]
|
70
|
+
|
71
|
+
def initialize
|
72
|
+
@settings = []
|
73
|
+
@arbitrary_lines = []
|
74
|
+
@data = []
|
75
|
+
@styles = []
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.construct(&block)
|
79
|
+
plot = new
|
80
|
+
|
81
|
+
block.call plot if block_given?
|
82
|
+
|
83
|
+
{ plot_settings: plot.to_gplot, plot_data: plot.store_datasets }
|
84
|
+
end
|
85
|
+
|
86
|
+
def method_missing(meth, *args)
|
87
|
+
set meth.id2name, *args
|
88
|
+
end
|
89
|
+
|
90
|
+
def set ( var, value = "" )
|
91
|
+
value = "\"#{value}\"" if QUOTED_METHODS.include? var unless value =~ /^'.*'$/
|
92
|
+
@settings << [ :set, var, value ]
|
93
|
+
end
|
94
|
+
|
95
|
+
def unset (var)
|
96
|
+
@settings << [ :unset, var ]
|
97
|
+
end
|
98
|
+
|
99
|
+
def to_gplot(io = '')
|
100
|
+
@settings.each { |setting| io += setting.map(&:to_s).join(' ') + "\n" }
|
101
|
+
@styles.each{ |style| io += style.to_s + "\n" }
|
102
|
+
@arbitrary_lines.each{ |line| io += line + "\n" }
|
103
|
+
|
104
|
+
io
|
105
|
+
end
|
106
|
+
|
107
|
+
def store_datasets(io = '')
|
108
|
+
if @data.size > 0
|
109
|
+
io += 'plot' + " #{ @data.map { |element| element.plot_args }.join(', ') } \n"
|
110
|
+
io += @data.map { |ds| ds.to_gplot }.compact.join("\n") + "\n"
|
111
|
+
end
|
112
|
+
|
113
|
+
io
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
class Gnuplot::Style
|
118
|
+
attr_accessor :linestyle, :linetype, :linewidth, :linecolor, :pointtype, :pointsize, :fill, :index
|
119
|
+
|
120
|
+
alias :ls :linestyle
|
121
|
+
alias :lt :linetype
|
122
|
+
alias :lw :linewidth
|
123
|
+
alias :lc :linecolor
|
124
|
+
alias :pt :pointtype
|
125
|
+
alias :ps :pointsize
|
126
|
+
alias :fs :fill
|
127
|
+
|
128
|
+
alias :ls= :linestyle=
|
129
|
+
alias :lt= :linetype=
|
130
|
+
alias :lw= :linewidth=
|
131
|
+
alias :lc= :linecolor=
|
132
|
+
alias :pt= :pointtype=
|
133
|
+
alias :ps= :pointsize=
|
134
|
+
alias :fs= :fill=
|
135
|
+
|
136
|
+
STYLES = [:ls, :lt, :lw, :lc, :pt, :ps, :fs]
|
137
|
+
|
138
|
+
def self.increment_index
|
139
|
+
@index ||= 0
|
140
|
+
@index += 1
|
141
|
+
end
|
142
|
+
|
143
|
+
def initialize
|
144
|
+
STYLES.each do |style|
|
145
|
+
send("#{style}=", nil)
|
146
|
+
end
|
147
|
+
|
148
|
+
yield self if block_given?
|
149
|
+
|
150
|
+
end
|
151
|
+
|
152
|
+
def to_s
|
153
|
+
str = ' '
|
154
|
+
|
155
|
+
STYLES.each do |style|
|
156
|
+
str += " #{style} #{send(style)}" if send(style)
|
157
|
+
end
|
158
|
+
|
159
|
+
str == ' ' ? '' : str
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
class Gnuplot::DataSet
|
164
|
+
attr_accessor :title, :with, :using, :data, :linewidth, :linecolor, :matrix, :smooth, :axes, :index, :linestyle
|
165
|
+
|
166
|
+
alias :ls :linestyle
|
167
|
+
|
168
|
+
def initialize(data = nil)
|
169
|
+
@data = data
|
170
|
+
@linestyle = nil
|
171
|
+
@title = nil
|
172
|
+
@with = nil
|
173
|
+
@using = nil
|
174
|
+
@linewidth = nil
|
175
|
+
@linecolor = nil
|
176
|
+
@matrix = nil
|
177
|
+
@smooth = nil
|
178
|
+
@axes = nil
|
179
|
+
@index = nil
|
180
|
+
|
181
|
+
yield self if block_given?
|
182
|
+
end
|
183
|
+
|
184
|
+
def notitle
|
185
|
+
'"No Title"'
|
186
|
+
end
|
187
|
+
|
188
|
+
def plot_args(io = '')
|
189
|
+
io += @data.is_a?(String) ? @data : "'-'"
|
190
|
+
#io += " index #{@index}" if @index
|
191
|
+
io += " using #{@using}" if @using
|
192
|
+
io += " axes #{@axes}" if @axes
|
193
|
+
io += " title #{@title ? "\"#{@title}\"" : notitle}"
|
194
|
+
io += " matrix #{@matrix}" if @matrix
|
195
|
+
io += " smooth #{@smooth}" if @smooth
|
196
|
+
io += " with #{@with}" if @with
|
197
|
+
io += " linecolor #{@linecolor}" if @linecolor
|
198
|
+
io += " line #{@index} linewidth #{@linewidth}" if @linewidth
|
199
|
+
io += " linestyle #{@linestyle.index}" if @linestyle
|
200
|
+
|
201
|
+
io
|
202
|
+
end
|
203
|
+
|
204
|
+
def to_gplot
|
205
|
+
return nil if @data.nil? || @data.is_a?(String)
|
206
|
+
|
207
|
+
@data.to_gplot
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
class Array
|
213
|
+
def to_gplot
|
214
|
+
if number_series?(self)
|
215
|
+
series_for_plot = ''
|
216
|
+
self.each { |elem| series_for_plot += "#{elem}\n" }
|
217
|
+
series_for_plot + 'e'
|
218
|
+
else
|
219
|
+
self[0].zip(self[1]).map{ |elem| elem.join(' ') }.join("\n") + "\ne\n"
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
private
|
224
|
+
|
225
|
+
def number_series?(data)
|
226
|
+
data.each do |elem|
|
227
|
+
return false unless elem.is_a?(Numeric)
|
228
|
+
end
|
229
|
+
|
230
|
+
true
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
class Matrix
|
235
|
+
def to_gplot
|
236
|
+
matrix_for_plot = ''
|
237
|
+
|
238
|
+
(0...self.column_size).each do |j|
|
239
|
+
(0...self.row_size).each do |i|
|
240
|
+
matrix_for_plot += "#{i} #{j} #{self[j,i]}\n" if self[j,i]
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
matrix_for_plot
|
245
|
+
end
|
246
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Grada
|
2
|
+
class HeatMap
|
3
|
+
def self.plot(x, opts, &block)
|
4
|
+
Gnuplot.open do
|
5
|
+
Gnuplot::Plot.construct do |plot|
|
6
|
+
block.call plot if block
|
7
|
+
|
8
|
+
plot.set "pm3d map"
|
9
|
+
plot.set "palette color"
|
10
|
+
plot.set "xrange [0:#{x.size-1}]"
|
11
|
+
plot.set "yrange [0:#{x.size-1}]"
|
12
|
+
plot.set "cbrange [#{opts[:min]}:#{opts[:max]}]"
|
13
|
+
plot.set "cblabel \"#{opts[:x_label]}\""
|
14
|
+
plot.set "palette model RGB"
|
15
|
+
plot.set "palette define"
|
16
|
+
|
17
|
+
plot.title opts[:title]
|
18
|
+
plot.data << Gnuplot::DataSet.new(Matrix.columns(x)) do |ds|
|
19
|
+
ds.with = opts[:with]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Grada
|
2
|
+
class Histogram < DefaultBase
|
3
|
+
def self.plot(x, opts, &block)
|
4
|
+
Gnuplot.open do
|
5
|
+
Gnuplot::Plot.construct do |plot|
|
6
|
+
block.call plot if block
|
7
|
+
|
8
|
+
width = ( x.max - x.min ) / x.size
|
9
|
+
|
10
|
+
plot.title opts[:title]
|
11
|
+
|
12
|
+
plot.set "style data histogram"
|
13
|
+
plot.xlabel opts[:x_label]
|
14
|
+
plot.ylabel "Frequency"
|
15
|
+
plot.set "style fill solid 0.5"
|
16
|
+
plot.set "xrange [#{x.min}:#{x.max}]"
|
17
|
+
plot.set "boxwidth #{ width * 0.1}"
|
18
|
+
plot.set "xtics #{x.min},#{(x.max-x.min)/5},#{x.max}"
|
19
|
+
plot.set "tics out nomirror"
|
20
|
+
|
21
|
+
plot.data << Gnuplot::DataSet.new(x) do |ds|
|
22
|
+
ds.with = 'boxes'
|
23
|
+
ds.title = opts[:x_label]
|
24
|
+
ds.using = '($1):(1.0)'
|
25
|
+
ds.smooth = 'freq'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.plot_html(x, opts)
|
32
|
+
opts[:filename] = create_html_dir(opts[:filename])
|
33
|
+
|
34
|
+
create_grada_json(opts, x)
|
35
|
+
|
36
|
+
File.open("#{opts[:filename]}.html",'w') do |f|
|
37
|
+
f << html_head
|
38
|
+
f << "<body>\n"
|
39
|
+
f << " <div class=grada_main>\n"
|
40
|
+
f << html_title(opts[:title])
|
41
|
+
f << html_graph
|
42
|
+
f << html_panel
|
43
|
+
f << " </div>"
|
44
|
+
f << "</body>"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: grada
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-08-19 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -50,7 +50,12 @@ extensions: []
|
|
50
50
|
extra_rdoc_files: []
|
51
51
|
files:
|
52
52
|
- lib/grada.rb
|
53
|
-
- lib/grada/
|
53
|
+
- lib/grada/graph.rb
|
54
|
+
- lib/grada/types/default_base.rb
|
55
|
+
- lib/grada/types/default.rb
|
56
|
+
- lib/grada/types/gnuplot.rb
|
57
|
+
- lib/grada/types/heat_map.rb
|
58
|
+
- lib/grada/types/histogram.rb
|
54
59
|
homepage: https://github.com/emfigo/grada
|
55
60
|
licenses:
|
56
61
|
- MIT
|
data/lib/grada/gnuplot.rb
DELETED
@@ -1,244 +0,0 @@
|
|
1
|
-
require 'matrix'
|
2
|
-
require 'open3'
|
3
|
-
|
4
|
-
class NoGnuPlotExecutableFound < RuntimeError; end
|
5
|
-
|
6
|
-
class Gnuplot
|
7
|
-
def self.candidate?(candidate)
|
8
|
-
return candidate if File::executable? candidate
|
9
|
-
|
10
|
-
ENV['PATH'].split(File::PATH_SEPARATOR).each do |dir|
|
11
|
-
possible_candidate = File::join dir, candidate.strip
|
12
|
-
|
13
|
-
return possible_candidate if File::executable? possible_candidate
|
14
|
-
end
|
15
|
-
|
16
|
-
nil
|
17
|
-
end
|
18
|
-
|
19
|
-
def self.find_exec(bin)
|
20
|
-
bin_list = RUBY_PLATFORM =~ /mswin|mingw/ ? [bin, "#{bin}.exe"] : [bin]
|
21
|
-
|
22
|
-
bin_list.each do |c|
|
23
|
-
exec = candidate?(c)
|
24
|
-
return exec if exec
|
25
|
-
end
|
26
|
-
|
27
|
-
nil
|
28
|
-
end
|
29
|
-
|
30
|
-
def self.gnuplot
|
31
|
-
gnu_exec = find_exec( ENV['RB_GNUPLOT'] || 'gnuplot' )
|
32
|
-
raise NoGnuPlotExecutableFound unless gnu_exec
|
33
|
-
gnu_exec
|
34
|
-
end
|
35
|
-
|
36
|
-
def self.open(persist = true, &block)
|
37
|
-
gnuplot_cmd = gnuplot
|
38
|
-
|
39
|
-
commands = yield
|
40
|
-
|
41
|
-
output = StringIO.new
|
42
|
-
Open3::popen3(gnuplot_cmd, '-persist') do |data_in, data_out, stderr, wait_th|
|
43
|
-
data_in << commands[:plot_settings]
|
44
|
-
data_in << commands[:plot_data]
|
45
|
-
|
46
|
-
data_in.flush
|
47
|
-
sleep 1
|
48
|
-
|
49
|
-
while true do
|
50
|
-
window = IO::popen('xprop -name "Gnuplot" WM_NAME 2>/dev/null').gets
|
51
|
-
break unless window
|
52
|
-
sleep 1
|
53
|
-
end
|
54
|
-
data_in.close
|
55
|
-
data_out.close
|
56
|
-
stderr.close
|
57
|
-
|
58
|
-
output.write(wait_th.value.exitstatus)
|
59
|
-
end
|
60
|
-
|
61
|
-
output.string
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
class Gnuplot::Plot
|
66
|
-
attr_accessor :cmd, :data, :settings, :styles, :arbitrary_lines
|
67
|
-
|
68
|
-
QUOTED_METHODS = [ "title", "output", "xlabel", "x2label", "ylabel", "y2label", "clabel", "cblabel", "zlabel" ]
|
69
|
-
|
70
|
-
def initialize
|
71
|
-
@settings = []
|
72
|
-
@arbitrary_lines = []
|
73
|
-
@data = []
|
74
|
-
@styles = []
|
75
|
-
end
|
76
|
-
|
77
|
-
def self.construct(&block)
|
78
|
-
plot = new
|
79
|
-
|
80
|
-
block.call plot if block_given?
|
81
|
-
|
82
|
-
{ plot_settings: plot.to_gplot, plot_data: plot.store_datasets }
|
83
|
-
end
|
84
|
-
|
85
|
-
def method_missing(meth, *args)
|
86
|
-
set meth.id2name, *args
|
87
|
-
end
|
88
|
-
|
89
|
-
def set ( var, value = "" )
|
90
|
-
value = "\"#{value}\"" if QUOTED_METHODS.include? var unless value =~ /^'.*'$/
|
91
|
-
@settings << [ :set, var, value ]
|
92
|
-
end
|
93
|
-
|
94
|
-
def unset (var)
|
95
|
-
@settings << [ :unset, var ]
|
96
|
-
end
|
97
|
-
|
98
|
-
def to_gplot(io = '')
|
99
|
-
@settings.each { |setting| io += setting.map(&:to_s).join(' ') + "\n" }
|
100
|
-
@styles.each{ |style| io += style.to_s + "\n" }
|
101
|
-
@arbitrary_lines.each{ |line| io += line + "\n" }
|
102
|
-
|
103
|
-
io
|
104
|
-
end
|
105
|
-
|
106
|
-
def store_datasets(io = '')
|
107
|
-
if @data.size > 0
|
108
|
-
io += 'plot' + " #{ @data.map { |element| element.plot_args }.join(', ') } \n"
|
109
|
-
io += @data.map { |ds| ds.to_gplot }.compact.join("\n") + "\n"
|
110
|
-
end
|
111
|
-
|
112
|
-
io
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
class Gnuplot::Style
|
117
|
-
attr_accessor :linestyle, :linetype, :linewidth, :linecolor, :pointtype, :pointsize, :fill, :index
|
118
|
-
|
119
|
-
alias :ls :linestyle
|
120
|
-
alias :lt :linetype
|
121
|
-
alias :lw :linewidth
|
122
|
-
alias :lc :linecolor
|
123
|
-
alias :pt :pointtype
|
124
|
-
alias :ps :pointsize
|
125
|
-
alias :fs :fill
|
126
|
-
|
127
|
-
alias :ls= :linestyle=
|
128
|
-
alias :lt= :linetype=
|
129
|
-
alias :lw= :linewidth=
|
130
|
-
alias :lc= :linecolor=
|
131
|
-
alias :pt= :pointtype=
|
132
|
-
alias :ps= :pointsize=
|
133
|
-
alias :fs= :fill=
|
134
|
-
|
135
|
-
STYLES = [:ls, :lt, :lw, :lc, :pt, :ps, :fs]
|
136
|
-
|
137
|
-
def self.increment_index
|
138
|
-
@index ||= 0
|
139
|
-
@index += 1
|
140
|
-
end
|
141
|
-
|
142
|
-
def initialize
|
143
|
-
STYLES.each do |style|
|
144
|
-
send("#{style}=", nil)
|
145
|
-
end
|
146
|
-
|
147
|
-
yield self if block_given?
|
148
|
-
|
149
|
-
end
|
150
|
-
|
151
|
-
def to_s
|
152
|
-
str = ' '
|
153
|
-
|
154
|
-
STYLES.each do |style|
|
155
|
-
str += " #{style} #{send(style)}" if send(style)
|
156
|
-
end
|
157
|
-
|
158
|
-
str == ' ' ? '' : str
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
class Gnuplot::DataSet
|
163
|
-
attr_accessor :title, :with, :using, :data, :linewidth, :linecolor, :matrix, :smooth, :axes, :index, :linestyle
|
164
|
-
|
165
|
-
alias :ls :linestyle
|
166
|
-
|
167
|
-
def initialize(data = nil)
|
168
|
-
@data = data
|
169
|
-
@linestyle = nil
|
170
|
-
@title = nil
|
171
|
-
@with = nil
|
172
|
-
@using = nil
|
173
|
-
@linewidth = nil
|
174
|
-
@linecolor = nil
|
175
|
-
@matrix = nil
|
176
|
-
@smooth = nil
|
177
|
-
@axes = nil
|
178
|
-
@index = nil
|
179
|
-
|
180
|
-
yield self if block_given?
|
181
|
-
end
|
182
|
-
|
183
|
-
def notitle
|
184
|
-
'"No Title"'
|
185
|
-
end
|
186
|
-
|
187
|
-
def plot_args(io = '')
|
188
|
-
io += @data.is_a?(String) ? @data : "'-'"
|
189
|
-
#io += " index #{@index}" if @index
|
190
|
-
io += " using #{@using}" if @using
|
191
|
-
io += " axes #{@axes}" if @axes
|
192
|
-
io += " title #{@title ? "\"#{@title}\"" : notitle}"
|
193
|
-
io += " matrix #{@matrix}" if @matrix
|
194
|
-
io += " smooth #{@smooth}" if @smooth
|
195
|
-
io += " with #{@with}" if @with
|
196
|
-
io += " linecolor #{@linecolor}" if @linecolor
|
197
|
-
io += " line #{@index} linewidth #{@linewidth}" if @linewidth
|
198
|
-
io += " linestyle #{@linestyle.index}" if @linestyle
|
199
|
-
|
200
|
-
io
|
201
|
-
end
|
202
|
-
|
203
|
-
def to_gplot
|
204
|
-
return nil if @data.nil? || @data.is_a?(String)
|
205
|
-
|
206
|
-
@data.to_gplot
|
207
|
-
end
|
208
|
-
end
|
209
|
-
|
210
|
-
class Array
|
211
|
-
def to_gplot
|
212
|
-
if number_series?(self)
|
213
|
-
series_for_plot = ''
|
214
|
-
self.each { |elem| series_for_plot += "#{elem}\n" }
|
215
|
-
series_for_plot + 'e'
|
216
|
-
else
|
217
|
-
self[0].zip(self[1]).map{ |elem| elem.join(' ') }.join("\n") + "\ne\n"
|
218
|
-
end
|
219
|
-
end
|
220
|
-
|
221
|
-
private
|
222
|
-
|
223
|
-
def number_series?(data)
|
224
|
-
data.each do |elem|
|
225
|
-
return false unless elem.is_a?(Numeric)
|
226
|
-
end
|
227
|
-
|
228
|
-
true
|
229
|
-
end
|
230
|
-
end
|
231
|
-
|
232
|
-
class Matrix
|
233
|
-
def to_gplot
|
234
|
-
matrix_for_plot = ''
|
235
|
-
|
236
|
-
(0...self.column_size).each do |j|
|
237
|
-
(0...self.row_size).each do |i|
|
238
|
-
matrix_for_plot += "#{i} #{j} #{self[j,i]}\n" if self[j,i]
|
239
|
-
end
|
240
|
-
end
|
241
|
-
|
242
|
-
matrix_for_plot
|
243
|
-
end
|
244
|
-
end
|