ctioga2 0.8 → 0.9

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/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