ctioga2 0.9 → 0.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/Changelog +27 -0
  2. data/bin/ct2-make-movie +92 -45
  3. data/lib/ctioga2/commands/doc/html.rb +1 -1
  4. data/lib/ctioga2/commands/doc/introspection.rb +52 -0
  5. data/lib/ctioga2/commands/doc/markup.rb +4 -0
  6. data/lib/ctioga2/commands/general-commands.rb +19 -0
  7. data/lib/ctioga2/commands/general-functions.rb +8 -1
  8. data/lib/ctioga2/commands/general-types.rb +14 -3
  9. data/lib/ctioga2/commands/parsers/file.rb +20 -3
  10. data/lib/ctioga2/data/backends/backends/math.rb +1 -1
  11. data/lib/ctioga2/data/backends/backends/text.rb +3 -3
  12. data/lib/ctioga2/data/point.rb +1 -1
  13. data/lib/ctioga2/graphics/elements.rb +1 -0
  14. data/lib/ctioga2/graphics/elements/curve2d.rb +5 -0
  15. data/lib/ctioga2/graphics/elements/primitive.rb +16 -14
  16. data/lib/ctioga2/graphics/elements/style-lists.rb +353 -0
  17. data/lib/ctioga2/graphics/elements/subplot.rb +17 -2
  18. data/lib/ctioga2/graphics/elements/tangent.rb +16 -17
  19. data/lib/ctioga2/graphics/styles.rb +1 -0
  20. data/lib/ctioga2/graphics/styles/axes.rb +63 -0
  21. data/lib/ctioga2/graphics/styles/base.rb +13 -0
  22. data/lib/ctioga2/graphics/styles/colorbrewer.rb +316 -0
  23. data/lib/ctioga2/graphics/styles/colormap.rb +47 -7
  24. data/lib/ctioga2/graphics/styles/image.rb +83 -0
  25. data/lib/ctioga2/graphics/styles/plot.rb +43 -7
  26. data/lib/ctioga2/graphics/styles/sets.rb +47 -5
  27. data/lib/ctioga2/graphics/styles/sheet.rb +18 -3
  28. data/lib/ctioga2/graphics/styles/texts.rb +49 -8
  29. data/lib/ctioga2/graphics/subplot-commands.rb +13 -13
  30. data/lib/ctioga2/graphics/types.rb +42 -10
  31. data/lib/ctioga2/graphics/types/boundaries.rb +3 -2
  32. data/lib/ctioga2/graphics/types/boxes.rb +13 -0
  33. data/lib/ctioga2/graphics/types/dimensions.rb +4 -1
  34. data/lib/ctioga2/graphics/types/location.rb +13 -2
  35. data/lib/ctioga2/graphics/types/point.rb +86 -0
  36. data/lib/ctioga2/metabuilder/types/styles.rb +2 -2
  37. data/lib/ctioga2/plotmaker.rb +3 -0
  38. data/lib/ctioga2/ruby.rb +49 -0
  39. data/lib/ctioga2/utils.rb +34 -0
  40. data/lib/ctioga2/version.rb +2 -2
  41. metadata +8 -4
data/Changelog CHANGED
@@ -1,3 +1,30 @@
1
+ ctioga2 (0.10)
2
+
3
+ * A draw-image function to include JPEG and PNG images
4
+ * Axes at origin (with at_x_origin and at_y_origin)
5
+ * A drawing-frame command to have pure drawing frames (no axes,
6
+ no labels + coordinates given by physical units)
7
+ * A $(point index ...) returning the index of the dataset point
8
+ * Commands for drawing the currently available style items (colors,
9
+ markers, line styles and sets)
10
+ * Inline Ruby code in command files and running of external Ruby code
11
+ (for function definition only, since data processing/plotting is not
12
+ yet available on the Ruby side)
13
+ * Auto sizing of titles and labels
14
+ * Alignment for texts with a given width
15
+ * Smarter collision detection: now labels stick out of the graph only if
16
+ you really ask for it !
17
+ * Color sets from colorbrewer2.org
18
+ * Improved color maps specifications, including the possibility to
19
+ specify color sets as color maps
20
+ * A --list-styles command to list (the names of) all stylistic things
21
+ (and in particular, defined sets)
22
+ * Symmetric color maps
23
+ * Various bug fixes and documentation updates
24
+ * [ct2-make-movie] multiple outputs at once
25
+
26
+ -- Vincent <vincent.fourmond@9online.fr> Sun 16 Mar 18:16:01 CET 2014
27
+
1
28
  ctioga2 (0.9)
2
29
 
3
30
  * A --set command to set variable values from the command-line
@@ -34,7 +34,55 @@ ct2 = "ctioga2"
34
34
 
35
35
 
36
36
  tmpdir = "tmp"
37
- target = nil
37
+
38
+
39
+ # The class in charge of running ffmpeg
40
+ class EncodingJob
41
+
42
+ # Target file
43
+ attr_accessor :target
44
+
45
+ # Bitrate
46
+ attr_accessor :bitrate
47
+
48
+ # Video codec
49
+ attr_accessor :codec
50
+
51
+ # Extra arguments
52
+ attr_accessor :extra_args
53
+
54
+ # Writes the given frame for encoding
55
+ def write_frame(data)
56
+ @encoder.write(data)
57
+ end
58
+
59
+ def add_args(args)
60
+ @extra_args ||= []
61
+ @extra_args += args
62
+ end
63
+
64
+ # Start job
65
+ def start_job(size)
66
+ ffmpeg_args = ["ffmpeg", '-y', "-f", "rawvideo",
67
+ "-r", "25", "-s", size, "-i", "-"]
68
+ if @bitrate
69
+ ffmpeg_args << "-b" << @bitrate
70
+ end
71
+ if @codec
72
+ ffmpeg_args << "-vcodec" << @codec
73
+ end
74
+
75
+ ffmpeg_args += @extra_args if @extra_args
76
+ ffmpeg_args << @target
77
+ p ffmpeg_args
78
+ @encoder = IO::popen(ffmpeg_args, "wb")
79
+ end
80
+
81
+ def close()
82
+ @encoder.close
83
+ end
84
+ end
85
+
38
86
 
39
87
  DimensionConversion = {
40
88
  "pt" => (72.0/72.27),
@@ -86,35 +134,56 @@ use_pdftoppm = false
86
134
  # file over and over again.
87
135
  store_all = true
88
136
 
89
- # Codec for ffmpeg
90
- codec = nil
91
137
 
92
- # Bitrate for ffmpeg
93
- bitrate = nil
138
+ encoders = [ EncodingJob.new ]
139
+ cur_enc = encoders.first
140
+
94
141
 
95
- fargs = []
96
142
  opts = OptionParser.new do |opts|
97
143
  opts.banner = "Usage: #$0 [options] file.ct2 arguments..."
98
144
 
145
+ ##################################################
146
+ # Encoding-related options
99
147
  opts.on("-t", "--target FILE", "Target video file") do |t|
100
- target = t
148
+ if cur_enc.target
149
+ cur_enc = EncodingJob.new
150
+ encoders << cur_enc
151
+ end
152
+ cur_enc.target = t
153
+ end
154
+
155
+ opts.on("-b", "--bitrate RATE", "Bitrate (indicative)") do |v|
156
+ cur_enc.bitrate = v
157
+ end
158
+
159
+ opts.on("", "--codec CODEC", "Target codec") do |v|
160
+ cur_enc.codec = v
161
+ end
162
+
163
+ opts.on("", "--ffmpeg-args ARGS", "Extra ffmpeg args") do |v|
164
+ cur_enc.add_args(Shellwords.split(v))
101
165
  end
102
166
 
167
+
168
+ ##################################################
169
+ #
170
+
103
171
  opts.on("", "--dir DIR", "Temporary directory for storage") do |t|
104
172
  tmpdir = t
105
173
  end
106
174
 
107
175
  opts.on("", "--version", "Prints version string") do
108
- puts "0.0"
176
+ puts "0.1"
109
177
  end
110
178
 
111
179
  opts.on("-p", "--[no-]pdftoppm", "Whether or not to use pdftoppm") do |t|
112
180
  use_pdftoppm = t
113
181
  end
114
182
 
115
- opts.on("", "--page-size SIZE",
183
+ opts.on("-r", "--page-size SIZE",
116
184
  "Set ctioga2 page size (in TeX dimensions)") do |v|
117
185
  ct2_size = page_size_to_points(v)
186
+ p ct2_size
118
187
  end
119
188
 
120
189
  opts.on("", "--resolution RES",
@@ -127,17 +196,6 @@ opts = OptionParser.new do |opts|
127
196
  store_all = v
128
197
  end
129
198
 
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
199
 
142
200
  end
143
201
 
@@ -160,7 +218,7 @@ puts "Producing #{ct2_page_size} PDF and converting to #{size} for the video"
160
218
 
161
219
 
162
220
  file = ARGV.shift
163
- target ||= file.sub(/(\.ct2)?$/, ".avi")
221
+ cur_enc.target ||= file.sub(/(\.ct2)?$/, ".avi")
164
222
  args = []
165
223
 
166
224
  for a in ARGV
@@ -184,22 +242,12 @@ FileUtils::mkpath(tmpdir)
184
242
 
185
243
 
186
244
  # @todo Use other encoding programs !
187
- # @todo bitrate control
188
245
 
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
246
+ # Start all encoders
247
+ for enc in encoders
248
+ enc.start_job(size)
196
249
  end
197
250
 
198
- ffmpeg_args += fargs
199
- ffmpeg_args << target
200
-
201
- encoder = IO::popen(ffmpeg_args, "wb")
202
-
203
251
  format = if store_all
204
252
  "#{tmpdir}/file-%04d"
205
253
  else
@@ -218,36 +266,35 @@ for f in args
218
266
  puts "Running: #{ct2_cmdline.join(" ")}"
219
267
  system(*ct2_cmdline)
220
268
 
269
+ b = nil
270
+
221
271
  if use_pdftoppm
222
- # @todo use other conversion programs !
223
272
  b1, s = Open3.capture2(
224
273
  "pdftoppm",
225
274
  "-r",
226
275
  "#{(conv*oversampling).to_i}",
227
276
  "#{name}.pdf",
228
277
  :stdin_data=>"", :binmode=>true)
229
- # @todo use other conversion programs !
230
278
  b, s = Open3.capture2("convert",
231
279
  "PPM:-",
232
- "-resize", size,
280
+ "-resize", "#{size}!",
233
281
  "-depth", "8", "YUV:-",
234
282
  :stdin_data=>b1, :binmode=>true)
235
283
  else
236
-
237
- # @todo use other conversion programs !
238
284
  b, s = Open3.capture2("convert",
239
285
  "-density", "#{(conv*oversampling).to_i}",
240
286
  "#{name}.pdf",
241
287
  "-alpha", "Remove",
242
- "-resize", size,
288
+ "-resize", "#{size}!",
243
289
  "-depth", "8", "YUV:-",
244
290
  :stdin_data=>"", :binmode=>true)
245
-
246
- # Use pdftoppm followed by convert, much much faster than just
247
- # convert (gs takes too much time)
248
291
  end
249
- encoder.write(b)
292
+ for enc in encoders
293
+ enc.write_frame(b)
294
+ end
250
295
  index += 1
251
296
  end
252
297
 
253
- encoder.close
298
+ for enc in encoders
299
+ enc.close
300
+ end
@@ -303,7 +303,7 @@ module CTioga2
303
303
  when String # plain URL target
304
304
  link = "#{it.target}"
305
305
  else
306
- raise "The link target should be either a group, a command or a type, but is a #{it.target.class}"
306
+ raise "The link target should be either a group, a command or a type, but is a #{it.target.class} (#{it.dbg.inspect})"
307
307
  end
308
308
  str << "<a href='#{link}'>#{it.to_s}</a>"
309
309
  when MarkedUpText::MarkupItemize
@@ -128,6 +128,47 @@ module CTioga2
128
128
  end
129
129
  end
130
130
 
131
+ # Lists all the stylistic things, and in particular the names
132
+ # of color sets, marker sets and the like.
133
+ #
134
+ # This function will hold more data with time.
135
+ def list_styles
136
+
137
+ puts "Available color sets:"
138
+ sets = Graphics::Styles::CurveStyleFactory::parameters['line_color'].sets
139
+ set_names = sets.keys.sort
140
+
141
+ sets_by_prefix = Utils.group_by_prefix(set_names, /(.*?)\d+$/)
142
+
143
+
144
+ for pref in sets_by_prefix.keys.sort
145
+ vals = Utils.suffix_numeric_sort(sets_by_prefix[pref])
146
+ puts " * #{vals.join(", ")} "
147
+ end
148
+
149
+ puts "\nAvailable marker sets:"
150
+ sets = Graphics::Styles::CurveStyleFactory::parameters['marker_marker'].sets
151
+ set_names = sets.keys.sort
152
+
153
+ sets_by_prefix = Utils.group_by_prefix(set_names, /(.*?)\d+$/)
154
+ for pref in sets_by_prefix.keys.sort
155
+ vals = Utils.suffix_numeric_sort(sets_by_prefix[pref])
156
+ puts " * #{vals.join(", ")} "
157
+ end
158
+
159
+ puts "\nAvailable line style sets:"
160
+ sets = Graphics::Styles::CurveStyleFactory::parameters['line_style'].sets
161
+ set_names = sets.keys.sort
162
+
163
+ sets_by_prefix = Utils.group_by_prefix(set_names, /(.*?)\d+$/)
164
+ for pref in sets_by_prefix.keys.sort
165
+ vals = Utils.suffix_numeric_sort(sets_by_prefix[pref])
166
+ puts " * #{vals.join(", ")} "
167
+ end
168
+
169
+
170
+ end
171
+
131
172
 
132
173
  protected
133
174
 
@@ -200,6 +241,17 @@ EOH
200
241
  ListTypesCmd.describe("List known types",
201
242
  <<EOH, IntrospectionGroup)
202
243
  List all types known to ctioga2
244
+ EOH
245
+
246
+ ListStylesCmd =
247
+ Cmd.new('list-styles', nil, '--list-styles',
248
+ [], RawOption) do |p, opts|
249
+ Introspection.new.list_styles()
250
+ end
251
+
252
+ ListStylesCmd.describe("List stylistic information",
253
+ <<EOH, IntrospectionGroup)
254
+ Lists all available color sets, marker sets and the like.
203
255
  EOH
204
256
 
205
257
  EditCommandCmd =
@@ -106,11 +106,15 @@ module CTioga2
106
106
  class MarkupLink < MarkupItem
107
107
  # The object target of the link
108
108
  attr_accessor :target
109
+
110
+ # For error reporting
111
+ attr_reader :dbg
109
112
 
110
113
  # _target_ is the name of the target, which can be of _type_
111
114
  # 'group', 'command', 'backend', 'type', 'function' and 'url'
112
115
  def initialize(doc, target, type)
113
116
  super(doc)
117
+ @dbg = [target, type]
114
118
  if type =~ /url/
115
119
  @target = target
116
120
  else
@@ -87,6 +87,25 @@ EOH
87
87
  Runs the given strings as commands, as if given from a command file.
88
88
  EOH
89
89
 
90
+
91
+ # Runs a ruby file
92
+ RunRubyFile =
93
+ Cmd.new("ruby-run", nil, "--ruby-run",
94
+ [ CmdArg.new('file')],
95
+ {}
96
+ ) do |plotmaker, file, opts|
97
+ # Work around bug on windows !
98
+ file = Utils::transcode_until_found(file)
99
+ Ruby::run_file(file)
100
+ end
101
+
102
+ RunRubyFile.describe("Run as Ruby code", <<EOH, GeneralGroup)
103
+ Reads the file and runs the Ruby code found inside, a bit like
104
+ Ruby would do with the @require@ command, excepted that @ctioga2@
105
+ does not follow Ruby's file searching rules: you have to specify the
106
+ full path.
107
+ EOH
108
+
90
109
  # Evaluate a series of commands.
91
110
  SetCommand = Cmd.new("set", nil, "--set",
92
111
  [ CmdArg.new('text'),
@@ -19,7 +19,7 @@ module CTioga2
19
19
  module Commands
20
20
 
21
21
  FuncEval = Function.new("eval", "Evaluate Ruby code") do |pm, code|
22
- eval(code)
22
+ Ruby::run_code(code)
23
23
  end
24
24
 
25
25
  FuncEval.describe <<EOD
@@ -35,6 +35,10 @@ They have no type. In particular, while this will work:
35
35
  # b := $(eval $(a) * 3)
36
36
  # # b is now 9
37
37
 
38
+ However, you need to use quotes if you must call functions:
39
+
40
+ # b := $(eval sqrt(2))
41
+
38
42
  Doing the same kind of things with text will be somewhat not satisfying:
39
43
 
40
44
  # a := "two words"
@@ -65,6 +69,8 @@ EOD
65
69
  point.y.to_s
66
70
  when "xy", "XY"
67
71
  "%g,%g" % point.point
72
+ when "index", "idx"
73
+ point.index
68
74
  else
69
75
  # The \ are not strictly speaking necessary, but they make
70
76
  # ruby-mode happier
@@ -82,6 +88,7 @@ dataset. Run this way:
82
88
 
83
89
  The first argument, here @x@ tells what we want to know about the
84
90
  given point: its @x@ value (passing @x@), its @y@ value (passing @y@),
91
+ its @index@ (by passing @index@ or @idx@)
85
92
  both its @x@ and @y@ ready to be used as coordinates for drawing
86
93
  commands using @xy@. For instance, to draw a circle marker right in
87
94
  the middle of the last dataset plotted, just run
@@ -121,14 +121,25 @@ EOD
121
121
 
122
122
  # A color map
123
123
  ColorMapType = CmdType.new('colormap', :colormap, <<EOD)
124
- A Z color map
124
+ A Z color map. It takes the form @Color1--Color2--Color3...@. All
125
+ colors can optionally be followed by a number. For instance, for
126
+ @Red--Blue--Pink--Green@, the colors are evenly spaced. In the case
127
+ @Red--Blue(0.1)--Pink(0.2)--Green@, the Blue to Pink strech is located
128
+ between Z values 0.1 and 0.2.
125
129
 
126
- \todo document !
130
+ If a prefix @hls:@ or @wheel:@ is present, then linear interpolation
131
+ is done in the HLS colorspace instead of the RGB one (the default).
132
+
133
+ If a suffix @:sym:@_value_ is present, then the colormap is symmetric
134
+ around that value.
135
+
136
+ It is also possible to directly use a {type: color-set}, in which case
137
+ eveything works as if the colors of the {type: color-set} had been
138
+ given directly, without Z values.
127
139
  EOD
128
140
 
129
141
  # This ones get here since they mess up with syntax highlighting
130
142
 
131
-
132
143
  # A stored dataset.
133
144
  StoredDatasetType = CmdType.new('stored-dataset',
134
145
  :string, <<EOD)
@@ -71,6 +71,7 @@ module CTioga2
71
71
  idx = 0
72
72
 
73
73
 
74
+ has_ruby = false
74
75
  ## @todo line counting ?
75
76
  for l in lines
76
77
  idx += 1
@@ -81,8 +82,10 @@ module CTioga2
81
82
  ## or make it more accurate ? The problem is that in a
82
83
  ## large command file, there may be things that look like
83
84
  ## old style commands ?
84
-
85
- if l =~ /^([a-z0-9-]+)\(/
85
+
86
+ if l =~ /^\s*ruby\s*$/
87
+ has_ruby = true
88
+ elsif l =~ /^([a-z0-9-]+)\(/ && (!has_ruby)
86
89
  path = io.respond_to?(:path) ? io.path : io.to_s
87
90
  warn { "Found old style (deprecated) commands in '#{path}', using old style parser"}
88
91
  return OldFileParser.new.
@@ -112,10 +115,24 @@ module CTioga2
112
115
 
113
116
  # Now, we rearrange the lines...
114
117
  idx = -1
118
+ ruby = false
115
119
  for l in parsed_lines
116
120
  idx += 1
117
121
  interpreter.context.parsing_file(nil, io, lines_indices[idx])
118
- if l =~ /^\s*([a-zA-Z0-9_-]+)\s*(\??)(=|:=)\s*(.*)/
122
+ if l =~ /^\s*ruby\s*$/
123
+ ruby = ""
124
+ elsif ruby
125
+ if l =~ /^\s*ruby\s+end\s*$/
126
+ begin
127
+ Ruby.run_code(ruby)
128
+ ruby = false
129
+ rescue Exception => e
130
+ fatal { "Error #{e.inspect} running inline Ruby code at #{interpreter.context}" }
131
+ end
132
+ else
133
+ ruby << l
134
+ end
135
+ elsif l =~ /^\s*([a-zA-Z0-9_-]+)\s*(\??)(=|:=)\s*(.*)/
119
136
  symbol = $1
120
137
  value = InterpreterString.parse_until_unquoted(StringIO.new($4),"\n", false)
121
138
  override = !($2 == '?')