redplot 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (2) hide show
  1. data/lib/redplot.rb +234 -0
  2. metadata +46 -0
@@ -0,0 +1,234 @@
1
+ # RedPlot is Copyright (c) 2011 Hugo Benichi <hbenichi@gmail.com>
2
+ #
3
+ # RedPlot is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # RedPlot is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9
+ # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10
+ # See the GNU General Public License for more details.
11
+ #
12
+ # You should have received a copy of the GNU General Public License along with RedPlot.
13
+ # If not, see <http://www.gnu.org/licenses/>.
14
+ #
15
+ # Description: a simple layer to gnuplot;
16
+ # can enhance any ruby object with plotting facilities;
17
+ #
18
+ # How it works: cf examples/;
19
+ # you can either mixin Redplot to any object;
20
+ # you can also directly use the Plotter class;
21
+ #
22
+
23
+
24
+ #main namespace
25
+ #also used for mixin
26
+ module RedPlot
27
+
28
+ VersionNumber, VersionDate = '0.1.2', '2011-04-04'
29
+
30
+ #helper class to store callbacks to the data and plot command options
31
+ class ToDraw
32
+ attr_reader :options, :callbacks
33
+ def initialize
34
+ reinit
35
+ end
36
+ def reinit
37
+ @options, @callbacks = [], []
38
+ self
39
+ end
40
+ #main client method
41
+ #expects a callback block that return an object with access to data to plat
42
+ #different object are possible (cf Plotter#formatdata)
43
+ def add(option="", &block)
44
+ unless block.nil?
45
+ @options << option
46
+ @callbacks << block
47
+ end
48
+ self
49
+ end
50
+ alias_method :clear, :reinit #add to test
51
+ end
52
+
53
+ #main class whose instances are wrapper to gnuplot process
54
+ #the instances manage the necessary ressources and have all the callable methods
55
+ #
56
+ #if an object is mixed with RedPlot, then an instance of Plotter is hooked to that object
57
+ #the instance of Plotter is then the path to gnuplot and helps to avoid name conflicts
58
+ class Plotter
59
+
60
+ attr_accessor :header, # stores in an array settings such as "set xrange [a,b]"
61
+ :path, # gives a path to save data and pritn graph
62
+ :command # determine the main plotting command: plot, splot, ...
63
+ attr_reader :todraw # holds callbacks to the data to be plotted
64
+
65
+ #destructor to close the gnuplot process
66
+ def self.release instance
67
+ instance.close
68
+ end
69
+
70
+ def method_missing(*args)
71
+ (scoped_header = @header ) << args.join(" ") + " "
72
+ args[0].to_s.tap do |eval_space|
73
+ eval_space.singleton_class.instance_eval do
74
+ define_method(:method_missing) do |*args|
75
+ scoped_header[-1] << args.join(" ") + " "
76
+ self
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ def initialize(args={}, &configuration_block)
83
+ @header = args[:header] || []
84
+ @path = args[:path] || 'redplot'
85
+ @command = args[:command] || 'plot'
86
+ @todraw = ToDraw.new
87
+ self.instance_eval &configuration_block if block_given?
88
+ self
89
+ end
90
+
91
+ #start the gnuplot process if @proc is nil
92
+ def start
93
+ @proc ||= IO.popen("gnuplot","w+").tap do |open|
94
+ ObjectSpace.define_finalizer(self) { open.close }
95
+ end
96
+ end
97
+
98
+ #directly write to the gnuplot process stdin
99
+ def <<( *args )
100
+ start.puts args
101
+ end
102
+
103
+ #main client method to draw data
104
+ #happens in 3 steps:
105
+ # 1) sends the header args to gnuplot
106
+ # 2) sends the plot command
107
+ # 3) sends the data one block at a time
108
+ def draw
109
+ start
110
+ @proc.puts @header
111
+ @proc.puts format_command( @command, @todraw.options )
112
+ @todraw.callbacks.each { |block| @proc.puts format_data( block.call ) }
113
+ self
114
+ end
115
+
116
+ #format the plot command
117
+ def format_command( command, options )
118
+ "%s '-' %s" % [command, options.join(",'-' ")] #not tested yet
119
+ end
120
+
121
+ #format the data from the callback blocks to fit gnuplot
122
+ #several interface are possible
123
+ # 1) a plain Array
124
+ # 2) an array of arrays, in which case these arrays are taken as data columns
125
+ # 3) anything with an each method
126
+ # the block send to each takes one or more Numerics and/or numbers hidden in string
127
+ def format_data( raw_data )
128
+ if raw_data.is_a? Array
129
+ if raw_data[0].is_a? Array
130
+ raw_data.transpose.map!{ |one_row| one_row.join " "}
131
+ else
132
+ raw_data
133
+ end
134
+ else
135
+ [].tap{ |data| raw_data.each { |*vals| data << vals.join(" ") } } #not tested yet
136
+ end.<< 'end'
137
+ end
138
+
139
+ #write to disc the result of format_Data
140
+ def save_data(path=@path)
141
+ File.open( path+".dat", "w+") do |file|
142
+ @todraw.callbacks.each do |block|
143
+ file.puts format_data( block.call ).tap{|ary| ary[-1] = ''}
144
+ end
145
+ end
146
+ self
147
+ end
148
+
149
+ #write to disc the content of @header and format_command
150
+ def save_script(path=@path)
151
+ File.open( path + ".scr", "w+") do |file|
152
+ file.puts @header
153
+ file.puts format_command( @command, @todraw.options )
154
+ end
155
+ self
156
+ end
157
+
158
+ #either draw in png or eps mode
159
+ def save_graph(type=:png, path=@path)
160
+ case type
161
+ when :png then save_png( path )
162
+ when :eps then save_eps( path )
163
+ end
164
+ end
165
+
166
+ #set gnuplot terminal to eps and draw
167
+ #!the terminal will stay in eps mode
168
+ def save_eps(path=@path)
169
+ start.puts "set term postscript eps color blacktext \"Helvetica\" 24",
170
+ "set output '#{path}.eps'"
171
+ draw
172
+ end
173
+
174
+ #set gnuplot terminal to png and draw
175
+ #!the terminal will stay in png mode
176
+ def save_png(path=@path)
177
+ start.puts 'set term png enhanced',
178
+ "set output '#{path}.png'"
179
+ draw
180
+ end
181
+
182
+ end
183
+
184
+ #all the mechanism for object extention happens here
185
+ class << self
186
+
187
+ #if a class includes RedPlot, RedPlot inserts itself in the class instead
188
+ def included who
189
+ insert in: who, as: :plot
190
+ end
191
+
192
+ #if an object extends RedPlot, RedPlot adds itself to the object via its eigenclass
193
+ def extended who
194
+ insert in: who.singleton_class, as: :plot
195
+ end
196
+
197
+ #this method takes a class and adds an attr_reader to it
198
+ #it also adds a lazy initialization reader with a shortcut name
199
+ # ! a call to the attr_reader before a call to the shorcut reader will return nil
200
+ def insert(args, &block)
201
+ target = args[:in]
202
+ name = args[:as] || :plot
203
+ target.class_eval do
204
+ attr_reader :gnuplot_wrapper
205
+ define_method(name) { @gnuplot_wrapper ||= RedPlot::Plotter.new(args,&block) }
206
+ end if target.is_a? Class
207
+ end
208
+
209
+ #returns a string with version number
210
+ def version
211
+ 'RedPlot version "%s" (%s)' % [RedPlot::VersionNumber, RedPlot::VersionDate]
212
+ end
213
+
214
+ end
215
+
216
+ end
217
+
218
+
219
+ class Array
220
+ #this provide a quick hook for Array instances
221
+ def plot!(args={})
222
+ args[:as] ||= :plot
223
+ args[:in] = self.singleton_class
224
+ data = self
225
+ RedPlot::insert(args) { todraw.add args[:style] {data} }
226
+ wrapper = self.send args[:as]
227
+ end
228
+ #hook RedPlot to the array and draw what is inside it
229
+ def plot_now!(args={})
230
+ self.plot!(args).draw
231
+ end
232
+ end
233
+
234
+
metadata ADDED
@@ -0,0 +1,46 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: redplot
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Hugo Benichi
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-06-05 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: redplot gives convenience handler methods from and to Ruby objects to
15
+ easily communicate with a gnuplot process
16
+ email: hugo.benichi@m4x.org
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - lib/redplot.rb
22
+ homepage: https://github.com/hugobenichi/redplot
23
+ licenses: []
24
+ post_install_message:
25
+ rdoc_options: []
26
+ require_paths:
27
+ - lib
28
+ required_ruby_version: !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ required_rubygems_version: !ruby/object:Gem::Requirement
35
+ none: false
36
+ requirements:
37
+ - - ! '>='
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ requirements: []
41
+ rubyforge_project:
42
+ rubygems_version: 1.8.11
43
+ signing_key:
44
+ specification_version: 3
45
+ summary: A simple layer wrapping around a gnuplot process.
46
+ test_files: []