ctioga2 0.9 → 0.10

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 (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 == '?')