redplot 0.0.2

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.
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: []