grada 2.2.2 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|