ctioga2 0.8 → 0.9

Sign up to get free protection for your applications and to get access to all the features.
data/Changelog CHANGED
@@ -1,3 +1,18 @@
1
+ ctioga2 (0.9)
2
+
3
+ * A --set command to set variable values from the command-line
4
+ * A basic ct2-make-movie script to facilitate the construction of movies
5
+ from a ctioga2 plot
6
+ * Definition of variables using ?= and ?:= that do not erase the
7
+ contents of variables if they already exist
8
+ * Makefile-like functions: eval and point
9
+ * More control on the position of major and minor ticks
10
+ * Automatic detection of text size for titles, labels and the like
11
+ * Minor bug fixes and documentation updates
12
+ * Now default to 6bp padding
13
+
14
+ -- Vincent <vincent.fourmond@9online.fr> Thu 6 Feb 21:19:34 CET 2014
15
+
1
16
  ctioga2 (0.8)
2
17
 
3
18
  * New plot type for making histograms (including cumulative histograms)
@@ -0,0 +1,253 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ =begin rdoc
4
+
5
+ This program is copyright 2014 by Vincent Fourmond.
6
+
7
+ This program is free software; you can redistribute it and/or modify
8
+ it under the terms of the GNU General Public License as published by
9
+ the Free Software Foundation; either version 2 of the License, or
10
+ (at your option) any later version.
11
+
12
+ This program is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU General Public License for more details.
16
+
17
+ You should have received a copy of the GNU General Public License
18
+ along with this program; if not, write to the Free Software
19
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20
+ =end
21
+
22
+
23
+ # This program is a wrapper around ctioga2 to make it easy to make
24
+ # movies.
25
+
26
+ require 'open3'
27
+ require 'fileutils'
28
+ require 'optparse'
29
+
30
+ require 'shellwords'
31
+
32
+ # Path to ctioga2 executable
33
+ ct2 = "ctioga2"
34
+
35
+
36
+ tmpdir = "tmp"
37
+ target = nil
38
+
39
+ DimensionConversion = {
40
+ "pt" => (72.0/72.27),
41
+ "bp" => 1.0,
42
+ "in" => 72.0,
43
+ "cm" => (72.0/2.54),
44
+ "mm" => (72.0/25.4),
45
+ }
46
+
47
+ def dim_to_points(dim)
48
+ if dim =~ /^\s*(\d+(\.\d*)?)\s*(pt|bp|cm|in|mm)\s*$/i
49
+ return $1.to_f * DimensionConversion[$3.downcase]
50
+ else
51
+ raise "Invalid dimension: #{dim}"
52
+ end
53
+ end
54
+
55
+ # Converts the given "real-size" resolution into postscript points
56
+ def page_size_to_points(spec)
57
+ if spec =~ /(.*)x(.*)/i
58
+ return [dim_to_points($1), dim_to_points($2)]
59
+ else
60
+ raise "Invalid page size: #{spec}"
61
+ end
62
+ end
63
+
64
+ # @todo Build a movie from a list of PDF files. Maybe less error
65
+ # checking than directly.
66
+
67
+ # The target resolution
68
+ res = [600,600]
69
+
70
+ # The target page size (in bp)
71
+ ct2_size = nil
72
+
73
+ # The corresponding number of points
74
+
75
+ # The conversion factor (between points and inches)
76
+ conv = 250.0
77
+
78
+ # The oversampling factor (to get something smooth in the end)
79
+ oversampling = 2
80
+
81
+ # Whether we use pdftoppm or not for the conversion. Much faster than
82
+ # convert
83
+ use_pdftoppm = false
84
+
85
+ # Whether we keep all intermediate PDF files, or we reuse the same
86
+ # file over and over again.
87
+ store_all = true
88
+
89
+ # Codec for ffmpeg
90
+ codec = nil
91
+
92
+ # Bitrate for ffmpeg
93
+ bitrate = nil
94
+
95
+ fargs = []
96
+ opts = OptionParser.new do |opts|
97
+ opts.banner = "Usage: #$0 [options] file.ct2 arguments..."
98
+
99
+ opts.on("-t", "--target FILE", "Target video file") do |t|
100
+ target = t
101
+ end
102
+
103
+ opts.on("", "--dir DIR", "Temporary directory for storage") do |t|
104
+ tmpdir = t
105
+ end
106
+
107
+ opts.on("", "--version", "Prints version string") do
108
+ puts "0.0"
109
+ end
110
+
111
+ opts.on("-p", "--[no-]pdftoppm", "Whether or not to use pdftoppm") do |t|
112
+ use_pdftoppm = t
113
+ end
114
+
115
+ opts.on("", "--page-size SIZE",
116
+ "Set ctioga2 page size (in TeX dimensions)") do |v|
117
+ ct2_size = page_size_to_points(v)
118
+ end
119
+
120
+ opts.on("", "--resolution RES",
121
+ "Set target resolution (overridden to some extent by page-size)") do |r|
122
+ r =~ /(\d+)x(\d+)/
123
+ res = [$1.to_f, $2.to_f]
124
+ end
125
+
126
+ opts.on("", "--[no-]store", "To store all or not..") do |v|
127
+ store_all = v
128
+ end
129
+
130
+ opts.on("-b", "--bitrate RATE", "Bitrate (indicative)") do |v|
131
+ bitrate = v
132
+ end
133
+
134
+ opts.on("", "--codec CODEC", "Target codec") do |v|
135
+ codec = v
136
+ end
137
+
138
+ opts.on("", "--ffmpeg-args ARGS", "Extra ffmpeg args") do |v|
139
+ fargs += Shellwords.split(v)
140
+ end
141
+
142
+ end
143
+
144
+ opts.parse!(ARGV)
145
+
146
+ # First, we choose the target page size and resolution.
147
+
148
+ if ct2_size
149
+ ct2_page_size = ct2_size.map { |x| "#{x}bp" }.join("x")
150
+ # maintain aspect ratio
151
+ res[1] = res[0] * ct2_size[1]/ct2_size[0]
152
+ conv = res[0]/ct2_size[0] * 72.27
153
+ else
154
+ ct2_page_size = res.map { |x| "#{x/conv}in"}.join("x")
155
+ end
156
+
157
+ size = res.map { |x| "#{x.to_i}"}.join("x")
158
+
159
+ puts "Producing #{ct2_page_size} PDF and converting to #{size} for the video"
160
+
161
+
162
+ file = ARGV.shift
163
+ target ||= file.sub(/(\.ct2)?$/, ".avi")
164
+ args = []
165
+
166
+ for a in ARGV
167
+ # Expansion !
168
+ if a =~ /^(.*)\.\.(.*):(\d+)\s*$/
169
+ s = $1.to_f
170
+ e = $2.to_f
171
+ nb = $3.to_i
172
+ nb.times do |i|
173
+ args << "#{s + (e-s)*i/(nb-1.0)}"
174
+ end
175
+ else
176
+ args << a
177
+ end
178
+ end
179
+
180
+
181
+ FileUtils::mkpath(tmpdir)
182
+
183
+ # Now, we compute the ctioga2 real size
184
+
185
+
186
+ # @todo Use other encoding programs !
187
+ # @todo bitrate control
188
+
189
+ ffmpeg_args = ["ffmpeg", '-y', "-f", "rawvideo",
190
+ "-r", "25", "-s", size, "-i", "-"]
191
+ if bitrate
192
+ ffmpeg_args << "-b" << bitrate
193
+ end
194
+ if codec
195
+ ffmpeg_args << "-vcodec" << codec
196
+ end
197
+
198
+ ffmpeg_args += fargs
199
+ ffmpeg_args << target
200
+
201
+ encoder = IO::popen(ffmpeg_args, "wb")
202
+
203
+ format = if store_all
204
+ "#{tmpdir}/file-%04d"
205
+ else
206
+ "#{tmpdir}/file"
207
+ end
208
+
209
+
210
+ index = 0
211
+ for f in args
212
+ name = format % index
213
+
214
+ ct2_cmdline = [ct2,
215
+ "--set", "arg", f,
216
+ "--set", "index", "#{index}",
217
+ "-f", file, "--name", name, "-r", ct2_page_size]
218
+ puts "Running: #{ct2_cmdline.join(" ")}"
219
+ system(*ct2_cmdline)
220
+
221
+ if use_pdftoppm
222
+ # @todo use other conversion programs !
223
+ b1, s = Open3.capture2(
224
+ "pdftoppm",
225
+ "-r",
226
+ "#{(conv*oversampling).to_i}",
227
+ "#{name}.pdf",
228
+ :stdin_data=>"", :binmode=>true)
229
+ # @todo use other conversion programs !
230
+ b, s = Open3.capture2("convert",
231
+ "PPM:-",
232
+ "-resize", size,
233
+ "-depth", "8", "YUV:-",
234
+ :stdin_data=>b1, :binmode=>true)
235
+ else
236
+
237
+ # @todo use other conversion programs !
238
+ b, s = Open3.capture2("convert",
239
+ "-density", "#{(conv*oversampling).to_i}",
240
+ "#{name}.pdf",
241
+ "-alpha", "Remove",
242
+ "-resize", size,
243
+ "-depth", "8", "YUV:-",
244
+ :stdin_data=>"", :binmode=>true)
245
+
246
+ # Use pdftoppm followed by convert, much much faster than just
247
+ # convert (gs takes too much time)
248
+ end
249
+ encoder.write(b)
250
+ index += 1
251
+ end
252
+
253
+ encoder.close
@@ -44,6 +44,9 @@ module CTioga2
44
44
  # Wether or not to ignore blacklisted commands
45
45
  attr_accessor :ignore_blacklisted
46
46
 
47
+ # The functions
48
+ attr_accessor :functions
49
+
47
50
  # Create a Doc object caring about the current state of
48
51
  # registered commands and such.
49
52
  def initialize
@@ -51,6 +54,7 @@ module CTioga2
51
54
  @groups = Interpreter::groups
52
55
  @types = Interpreter::types
53
56
  @backends = Data::Backends::Backend::list_backends
57
+ @functions = Function::functions
54
58
 
55
59
  @ignore_blacklisted = ! (ENV.key?("CT2_DEV") &&
56
60
  ! ENV["CT2_DEV"].empty?)
@@ -49,24 +49,39 @@ module CTioga2
49
49
  Writes a manual page based on a template
50
50
  EOH
51
51
 
52
+ WriteHTMLOptions = {
53
+ 'page-menu' => CmdArg.new('text')
54
+ }
52
55
 
53
56
  WriteHTMLCommands =
54
57
  Cmd.new("write-html-commands", nil, "--write-html-commands",
55
- []) do |plotmaker|
58
+ [], WriteHTMLOptions) do |plotmaker, opts|
56
59
  html = HTML.new(plotmaker.interpreter.doc)
57
- html.write_commands()
60
+ html.write_commands(opts)
58
61
  end
59
62
 
60
63
  WriteHTMLCommands.describe("HTML documentation for group and commands",
61
64
  <<EOH, DocumentationGenerationGroup)
62
65
  Prints the HTML documentation for group and commands to standard output.
66
+ EOH
67
+
68
+ WriteHTMLFunctions =
69
+ Cmd.new("write-html-functions", nil, "--write-html-functions",
70
+ [], WriteHTMLOptions) do |plotmaker, opts|
71
+ html = HTML.new(plotmaker.interpreter.doc)
72
+ html.write_functions(opts)
73
+ end
74
+
75
+ WriteHTMLFunctions.describe("HTML documentation for functions",
76
+ <<EOH, DocumentationGenerationGroup)
77
+ Prints the HTML documentation for functions.
63
78
  EOH
64
79
 
65
80
  WriteHTMLTypes =
66
81
  Cmd.new("write-html-types", nil, "--write-html-types",
67
- []) do |plotmaker|
82
+ [], WriteHTMLOptions) do |plotmaker, opts|
68
83
  html = HTML.new(plotmaker.interpreter.doc)
69
- html.write_types()
84
+ html.write_types(opts)
70
85
  end
71
86
 
72
87
  WriteHTMLTypes.describe("HTML documentation for types",
@@ -76,9 +91,9 @@ EOH
76
91
 
77
92
  WriteHTMLBackends =
78
93
  Cmd.new("write-html-backends", nil, "--write-html-backends",
79
- []) do |plotmaker|
94
+ [], WriteHTMLOptions) do |plotmaker, opts|
80
95
  html = HTML.new(plotmaker.interpreter.doc)
81
- html.write_backends()
96
+ html.write_backends(opts)
82
97
  end
83
98
 
84
99
  WriteHTMLBackends.describe("HTML documentation for backends",
@@ -89,9 +104,9 @@ EOH
89
104
 
90
105
  WriteHTMLCommandLineOptions =
91
106
  Cmd.new("write-html-commandline", nil, "--write-html-commandline",
92
- []) do |plotmaker|
107
+ [], WriteHTMLOptions) do |plotmaker, opts|
93
108
  html = HTML.new(plotmaker.interpreter.doc)
94
- html.write_command_line_options()
109
+ html.write_command_line_options(opts)
95
110
  end
96
111
 
97
112
  WriteHTMLCommandLineOptions.describe("HTML documentation for types",
@@ -46,46 +46,87 @@ module CTioga2
46
46
  @types_url = "types.html"
47
47
  @commands_url = "commands.html"
48
48
  @backends_url = "backends.html"
49
+ @functions_url = "functions.html"
49
50
  end
50
51
 
51
- # Ouputs HTML code to document all groups and commands
52
- def write_commands(out = STDOUT)
53
- cmds, groups = @doc.documented_commands
52
+ def write_page_menu(opts, out)
53
+ if !opts['page-menu'] or opts['page-menu'] =~ /menu|full/i
54
+ yield out
55
+ end
56
+ end
54
57
 
55
- out.puts "<div class='quick-jump'>"
56
- out.puts "Quick jump to a specific group of commands:\n"
57
- out.puts "<ul>\n"
58
- for g in groups
59
- out.puts "<li><a href='#group-#{g.id}'>#{g.name}</a></li>\n"
58
+ def write_page(opts, out)
59
+ if !opts['page-menu'] or opts['page-menu'] =~ /page|full/i
60
+ yield out
60
61
  end
61
- out.puts "</ul>\n"
62
- out.puts "</div>"
62
+ end
63
+
64
+ def write_functions(opts, out = STDOUT)
65
+ funcs = @doc.functions
66
+ names = funcs.keys.sort
63
67
 
64
- for g in groups
65
- out.puts
66
- out.puts "<h3 class='group' id='group-#{g.id}'>#{g.name}</h3>"
67
- out.puts markup_to_html(g.description)
68
+ write_page_menu(opts, out) do |out|
69
+ out.puts "<div class='quick-jump'>"
70
+ out.puts "<h3>Quick jump</h3>"
71
+ out.puts "<ul>\n"
72
+ for n in names
73
+ out.puts "<li><a href='#func-#{n}'>#{n}</a></li>\n"
74
+ end
75
+ out.puts "</ul>\n"
76
+ out.puts "</div>"
77
+ end
78
+ write_page(opts, out) do |out|
79
+ for n in names
80
+ f = funcs[n]
81
+ out.puts
82
+ out.puts "<h3 class='function' id='func-#{n}'>#{n} - #{f.short_description}</h3>"
83
+ out.puts markup_to_html(f.description)
84
+ end
85
+ end
86
+ end
68
87
 
69
- commands = cmds[g].sort {|a,b|
70
- a.name <=> b.name
71
- }
72
-
73
- out.puts "<p>"
74
- out.puts "<span class='bold'>Available commands:</span>\n"
75
- out.puts commands.map {|c|
76
- "<a href='#command-#{c.name}'><code>#{c.name}</code></a>"
77
- }.join(' ')
78
- out.puts "</p>"
88
+ # Ouputs HTML code to document all groups and commands
89
+ def write_commands(opts, out = STDOUT)
90
+ cmds, groups = @doc.documented_commands
79
91
 
80
- for cmd in commands
81
- out.puts
82
- out.puts command_documentation(cmd)
92
+ write_page_menu(opts, out) do |out|
93
+ out.puts "<div class='quick-jump'>"
94
+ out.puts "<h3>Quick jump</h3>"
95
+ out.puts "<ul>\n"
96
+ for g in groups
97
+ out.puts "<li><a href='#group-#{g.id}'>#{g.name}</a></li>\n"
98
+ end
99
+ out.puts "</ul>\n"
100
+ out.puts "</div>"
101
+ end
102
+
103
+ write_page(opts, out) do |out|
104
+ for g in groups
105
+ out.puts
106
+ out.puts "<h3 class='group' id='group-#{g.id}'>#{g.name}</h3>"
107
+ out.puts markup_to_html(g.description)
108
+
109
+ commands = cmds[g].sort {|a,b|
110
+ a.name <=> b.name
111
+ }
112
+
113
+ out.puts "<p>"
114
+ out.puts "<span class='bold'>Available commands:</span>\n"
115
+ out.puts commands.map {|c|
116
+ "<a href='#command-#{c.name}'><code>#{c.name}</code></a>"
117
+ }.join(' ')
118
+ out.puts "</p>"
119
+
120
+ for cmd in commands
121
+ out.puts
122
+ out.puts command_documentation(cmd)
123
+ end
83
124
  end
84
125
  end
85
126
  end
86
127
 
87
128
  # Write a HTML table documenting all command-line options.
88
- def write_command_line_options(out = STDOUT)
129
+ def write_command_line_options(opts, out = STDOUT)
89
130
  cmds, groups = @doc.documented_commands
90
131
 
91
132
  out.puts "<table>"
@@ -108,52 +149,60 @@ module CTioga2
108
149
 
109
150
 
110
151
  # Ouputs HTML code to document all types
111
- def write_types(out = STDOUT)
152
+ def write_types(opts, out = STDOUT)
112
153
  types = @doc.types.sort.map { |d| d[1]}
113
154
 
114
155
 
115
- out.puts "<div class='quick-jump'>"
116
- out.puts "Quick jump to a specific type:\n"
117
- out.puts "<ul>\n"
118
- for t in types
119
- out.puts "<li><a href='#type-#{t.name}'>#{t.name}</a></li>\n"
156
+ write_page_menu(opts, out) do |out|
157
+ out.puts "<div class='quick-jump'>"
158
+ out.puts "<h3>Quick jump</h3>"
159
+ out.puts "<ul>\n"
160
+ for t in types
161
+ out.puts "<li><a href='#type-#{t.name}'>#{t.name}</a></li>\n"
162
+ end
163
+ out.puts "</ul>\n"
164
+ out.puts "</div>"
120
165
  end
121
- out.puts "</ul>\n"
122
- out.puts "</div>"
123
166
 
124
- for t in types
125
- out.puts
126
- out.puts "<h4 id='type-#{t.name}' class='type'>#{t.name}</h4>\n"
127
- out.puts markup_to_html(t.description)
128
- out.puts # There is no need to wrap the markup
129
- # in a paragraph.
167
+ write_page(opts, out) do |out|
168
+ for t in types
169
+ out.puts
170
+ out.puts "<h4 id='type-#{t.name}' class='type'>#{t.name}</h4>\n"
171
+ out.puts markup_to_html(t.description)
172
+ out.puts # There is no need to wrap the markup
173
+ # in a paragraph.
174
+ end
130
175
  end
131
176
  end
132
177
 
133
178
 
134
179
  # Ouputs HTML code to all backends
135
- def write_backends(out = STDOUT)
180
+ def write_backends(opts, out = STDOUT)
136
181
  backends = @doc.backends.sort.map { |d| d[1]}
137
182
 
138
183
 
139
- out.puts "<div class='quick-jump'>"
140
- out.puts "Quick jump to a specific backend:\n"
141
- out.puts "<ul>\n"
142
- for b in backends
143
- out.puts "<li><a href='#backend-#{b.name}'>#{b.name}</a></li>\n"
184
+ write_page_menu(opts, out) do |out|
185
+ out.puts "<div class='quick-jump'>"
186
+ out.puts "<h3>Quick jump</h3>"
187
+ out.puts "<ul>\n"
188
+ for b in backends
189
+ out.puts "<li><a href='#backend-#{b.name}'>#{b.name}</a></li>\n"
190
+ end
191
+ out.puts "</ul>\n"
192
+ out.puts "</div>"
144
193
  end
145
- out.puts "</ul>\n"
146
- out.puts "</div>"
147
-
148
- for b in backends
149
- out.puts
150
- out.puts "<h3 id='backend-#{b.name}' class='backend'><code>#{b.name}</code>: #{b.long_name}</h3>\n"
151
- out.puts markup_to_html(b.description)
152
- out.puts
153
- for param in b.param_list
154
- out.puts "<h4 id='backend-#{b.name}-#{param.name}'>Parameter: #{param.name}</h4>"
155
- out.puts "<p><code>/#{param.name}=<a href='#{@types_url}#type-#{param.type.name}'>#{param.type.name}</a></p>"
156
- out.puts markup_to_html(param.description)
194
+
195
+ write_page(opts, out) do |out|
196
+ for b in backends
197
+ out.puts
198
+ out.puts "<h3 id='backend-#{b.name}' class='backend'><code>#{b.name}</code>: #{b.long_name}</h3>\n"
199
+ out.puts markup_to_html(b.description)
200
+ out.puts
201
+ for param in b.param_list
202
+ out.puts "<h4 id='backend-#{b.name}-#{param.name}'>Parameter: #{param.name}</h4>"
203
+ out.puts "<p><code>/#{param.name}=<a href='#{@types_url}#type-#{param.type.name}'>#{param.type.name}</a></code></p>"
204
+ out.puts markup_to_html(param.description)
205
+ end
157
206
  end
158
207
  end
159
208
  end
@@ -243,6 +292,8 @@ module CTioga2
243
292
  case it.target
244
293
  when Command
245
294
  link = "#{@commands_url}#command-#{it.target.name}"
295
+ when Function
296
+ link = "#{@functions_url}#func-#{it.target.name}"
246
297
  when CommandGroup
247
298
  link = "#{@commands_url}#group-#{it.target.id}"
248
299
  when CommandType