~ubuntu-branches/ubuntu/oneiric/ctioga2/oneiric

« back to all changes in this revision

Viewing changes to lib/ctioga2/plotmaker.rb

  • Committer: Bazaar Package Importer
  • Author(s): Vincent Fourmond
  • Date: 2011-01-24 21:36:06 UTC
  • Revision ID: james.westby@ubuntu.com-20110124213606-9ettx0ugl83z0bzp
Tags: upstream-0.1
ImportĀ upstreamĀ versionĀ 0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# plotmaker.rb: the main class for ctioga
 
2
# copyright (c) 2006, 2007, 2008, 2009, 2010 by Vincent Fourmond
 
3
  
 
4
# This program is free software; you can redistribute it and/or modify
 
5
# it under the terms of the GNU General Public License as published by
 
6
# the Free Software Foundation; either version 2 of the License, or
 
7
# (at your option) any later version.
 
8
  
 
9
# This program is distributed in the hope that it will be useful,
 
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
# GNU General Public License for more details (in the COPYING file).
 
13
 
 
14
 
 
15
# TODO, the main one:
 
16
 
17
# It currently is a pain to make complex plots with ctioga. A real
 
18
# pain. What could be done to improve the situation ?
 
19
 
20
# * hide the difference between edges and axes.
 
21
# * the layout mechanism is not comfortable enough to work with, especially
 
22
#   with the need for relative positioning.
 
23
#
 
24
# Would it be possible to allow for the 'real size' to be determined
 
25
# *afterwards* ??? Difficult !
 
26
 
 
27
# TODO, an even bigger one:
 
28
# Switch to a real command-based plotting program:
 
29
#  - any single operation that is realized by ctioga would be a command
 
30
#  - every single of these commands would take a given (fixed) number of
 
31
#    parameters (we should take care about boolean stuff)
 
32
#  - every command would be of course reachable as command-line options
 
33
#    but it could also be within files
 
34
#  - in these files, provide an additional mechanism for quickly defining
 
35
#    variables and do variable substitution.
 
36
#  - one command (plus arguments) per line, with provisions for
 
37
#    line-splitting
 
38
#  - allow some kind of 'include' directives (that would also be used for
 
39
#    cmdline inclusion of files)
 
40
#  - command-line arguments and command files could intermix (that *would*
 
41
#    be fun, since it would allow very little changes to a command-line
 
42
#    to change significantly the look of a file...!)
 
43
#  - LONG TERM: allow conditionals and variable
 
44
#    definition/substitution on command-line ?
 
45
#  - Use typed variables, converted into string when substitution occurs,
 
46
#    but manipulable as *typed* before ?? proposed syntax:
 
47
#    type: variable = contents ?
 
48
#
 
49
#  Each command could take *typed* arguments. That would allow typed
 
50
#  variables along with a string-to-type conversion ? (is that useful
 
51
#  ?) NO. Commands take String. And that is fine...
 
52
#
 
53
#  Provide *optional* hash-like arguments that probably could not be
 
54
#  used in the command-line, but could be in the file.
 
55
#
 
56
#  Provide self-documentation in each and every command
 
57
#
 
58
#  Manipulations of a buffer stack - including mathematical
 
59
#  expressions; provide commands to only *load* a file, but not
 
60
#  necessarily draw it.
 
61
#
 
62
#  Provide a way to 'save' a command-line into a command-file.
 
63
#
 
64
#  Write as many test suites as possible ??
 
65
#
 
66
#  Merge Metabuilder and Backends into the ctioga code base. There's
 
67
#  no need for extra complexity.
 
68
#
 
69
#  That requires a huge amount of work, but on the other hand, that
 
70
#  would be much more satisfactory than the current mess.
 
71
#
 
72
#  Commands would be part of "groups".
 
73
#
 
74
#  Release a new version of ctioga before that.
 
75
#
 
76
#  Don't rely on huge mess of things !
 
77
 
 
78
# IDEAS:
 
79
#
 
80
# * write a :point type that would parse figure/frame/page coordinates + maybe
 
81
#   arbitrary additions ?
 
82
# * drop the layout system, but instead write a simple plotting system:
 
83
#   - start the image as a figure
 
84
#   - start a subplot in the full figure if nothing was specified before the
 
85
#     first dataset
 
86
#   - start subplots manually using --inset or things of this spirit
 
87
#   - maybe, for the case when subplots were manually specified, resize
 
88
#     the graph so it fits ? (difficult, especially if the positions/sizes
 
89
#     are relative... but trivial if that isn't the case. Maybe provide
 
90
#     a autoresize function for that ? Or do it automatically if all the
 
91
#     toplevel (sub)plot positions are absolute ?)
 
92
#     
 
93
#   This scheme would allow for a relatively painless way to draw graphs...
 
94
#
 
95
# * Possibly wite 
 
96
 
 
97
 
 
98
 
 
99
# \todo make --xrange automatically select the range for the --math
 
100
# backend unless another range was explicitly specified.
 
101
 
 
102
require 'ctioga2/utils'
 
103
require 'ctioga2/log'
 
104
 
 
105
require 'shellwords'
 
106
 
 
107
# Maybe, maybe, maybe... We need tioga ?
 
108
require 'Tioga/FigureMaker'
 
109
 
 
110
 
 
111
# Command interpreter
 
112
require 'ctioga2/commands/interpreter'
 
113
# Various global scope commands:
 
114
require 'ctioga2/commands/general-commands'
 
115
# Introspection...
 
116
require 'ctioga2/commands/doc/introspection'
 
117
require 'ctioga2/commands/doc/documentation-commands'
 
118
 
 
119
 
 
120
# Data handling
 
121
require 'ctioga2/data/dataset'
 
122
require 'ctioga2/data/stack'
 
123
require 'ctioga2/data/backends/factory'
 
124
 
 
125
 
 
126
# Graphics
 
127
require 'ctioga2/graphics/root'
 
128
require 'ctioga2/graphics/styles'
 
129
require 'ctioga2/graphics/generator'
 
130
 
 
131
 
 
132
# Miscellaneous
 
133
require 'ctioga2/postprocess'
 
134
 
 
135
 
 
136
## \mainpage CTioga2's code documentation.
 
137
# This module contains all the classes used by ctioga
 
138
#
 
139
#
 
140
# This holds the main page for CTioga2 code documentation. Most
 
141
# interesting classes/namespaces are:
 
142
 
143
# * CTioga2::PlotMaker
 
144
# * CTioga2::Graphics
 
145
# * CTioga2::Commands
 
146
# * CTioga2::Data
 
147
#
 
148
# Have fun hacking...
 
149
module CTioga2
 
150
 
 
151
  Version::register_svn_info('$Revision: 244 $', '$Date: 2011-01-23 23:36:02 +0100 (Sun, 23 Jan 2011) $')
 
152
 
 
153
  # This class is the core of ctioga. It parses the command-line arguments,
 
154
  # reads all necessary files and plots graphs. Most of its functionality
 
155
  # is delegated into classes.
 
156
  #
 
157
  # \todo An important point would be to provide a facility that holds
 
158
  # all the default values. To each would be assigned a given name,
 
159
  # and programs would only use something like
 
160
  # \code
 
161
  # value = Default::value('stuff')
 
162
  # \endcode
 
163
  # 
 
164
  # Setting up defaults would only be a question of using one single
 
165
  # command (with admittedly many optional arguments)
 
166
  class PlotMaker
 
167
 
 
168
    # Include logging facilities for ctioga2
 
169
    include CTioga2::Log
 
170
 
 
171
    # The Commands::Interpreter object which runs all the commands.
 
172
    attr_accessor :interpreter
 
173
 
 
174
    # The Data::DataStack object that manipulates Dataset objects
 
175
    attr_accessor :data_stack
 
176
 
 
177
    # The Graphics::RootObject in charge of holding all things that
 
178
    # will eventually get drawn
 
179
    attr_accessor :root_object
 
180
 
 
181
    # A Graphics::CurveGenerator object in charge of producing
 
182
    # suitable elements to be added to the Graphics::RootObject
 
183
    attr_accessor :curve_generator
 
184
 
 
185
    # Below are simple plot attributes. Maybe they should be in their
 
186
    # own namespace.
 
187
 
 
188
    # The name of the figure
 
189
    attr_accessor :figure_name
 
190
 
 
191
    # The output directory
 
192
    attr_accessor :output_directory
 
193
 
 
194
    # Additional preamble for LaTeX output
 
195
    attr_accessor :latex_preamble
 
196
 
 
197
    # What happens to generated PDF files (a PostProcess object)
 
198
    attr_accessor :postprocess
 
199
 
 
200
    # Whether or not to include the command-line used to produce the
 
201
    # file in the target PDF file.
 
202
    attr_accessor :mark
 
203
 
 
204
    # Whether intermediate files are cleaned up automatically
 
205
    # afterwards or not...
 
206
    attr_accessor :cleanup
 
207
 
 
208
    # The stack of CurveStyle objects that were used so far.
 
209
    attr_accessor :curve_style_stack
 
210
    
 
211
 
 
212
    # The first instance of PlotMaker created
 
213
    @@first_plotmaker_instance = nil
 
214
 
 
215
    # Returns the first created instance of PlotMaker. This sounds
 
216
    # less object-oriented, yes, but that can come in useful some
 
217
    # times.
 
218
    def self.plotmaker
 
219
      return @@first_plotmaker_instance
 
220
    end
 
221
 
 
222
 
 
223
    # Setting up of the PlotMaker object
 
224
    def initialize
 
225
      CTioga2::Log::init_logger
 
226
      @data_stack = Data::DataStack.new
 
227
      @root_object = Graphics::RootObject.new
 
228
      @interpreter = Commands::Interpreter.new(self)
 
229
      @curve_generator = Graphics::CurveGenerator.new
 
230
 
 
231
      # Figure name:
 
232
      @figure_name = nil
 
233
 
 
234
      # Original preamble
 
235
      @latex_preamble = ""
 
236
 
 
237
      @postprocess = PostProcess.new
 
238
 
 
239
      # Make sure it is registered
 
240
      @@first_plotmaker_instance ||= self
 
241
 
 
242
      # We mark by default, as it comes dead useful.
 
243
      @mark = true
 
244
 
 
245
      # Remove intermediate files by default.
 
246
      @cleanup = true
 
247
 
 
248
      # Make curve style stack empty
 
249
      @curve_style_stack = []
 
250
    end
 
251
 
 
252
    # ctioga's entry point.
 
253
    def run(command_line)
 
254
 
 
255
      # The main catch-all around the plot:
 
256
      begin
 
257
        @command_line = command_line.dup
 
258
        if ENV.key? 'CTIOGA2_PRE'
 
259
          command_line.unshift(*Shellwords.shellwords(ENV['CTIOGA2_PRE']))
 
260
        end
 
261
        
 
262
        if ENV.key? 'CTIOGA2_POST'
 
263
          command_line.push(*Shellwords.shellwords(ENV['CTIOGA2_POST']))
 
264
        end
 
265
        
 
266
        @interpreter.run_command_line(command_line)
 
267
        
 
268
        # Now, draw the main figure
 
269
        file = draw_figure(@figure_name || "Plot-%03d", true)
 
270
      rescue SystemExit => e
 
271
        # We special-case the exit exception ;-)...
 
272
      rescue Exception => e
 
273
        debug { format_exception(e) }
 
274
        fatal { "#{e.message}" }
 
275
      end
 
276
    end
 
277
 
 
278
    # Flushes the current root object and starts a new one:
 
279
    def reset_graphics
 
280
      draw_figure(@figure_name || "Plot-%03d", true)
 
281
 
 
282
      @root_object = Graphics::RootObject.new
 
283
      @curve_generator = Graphics::CurveGenerator.new
 
284
    end
 
285
    
 
286
    # Returns a quoted version of the command line, that possibly
 
287
    # could be used again to reproduce the same results.
 
288
    def quoted_command_line
 
289
      quoted_args = @command_line.collect do |s|
 
290
        Utils::shell_quote_string(s)
 
291
      end.join ' '
 
292
      
 
293
      return "#{File.basename($0)} #{quoted_args}"
 
294
    end
 
295
 
 
296
    # Draws the figure currently accumulated in the #root_object.  It
 
297
    # returns the path of the PDF file produced.
 
298
    #
 
299
    # If _figname_ contains a % sign, it will be interpreted as a
 
300
    # format, and ctioga will attempt to find the first numbered file
 
301
    # that does not exists.
 
302
    #
 
303
    # \todo
 
304
    # * cleanup or not ?
 
305
    def draw_figure(figname = "Plot-%03d", last = false)
 
306
      return if @root_object.empty?
 
307
      
 
308
      if figname =~ /%/
 
309
        i = 0
 
310
        prev = figname.dup
 
311
        while true
 
312
          f = figname % i
 
313
          if f == prev
 
314
            figname = f
 
315
            break
 
316
          end
 
317
          if File::exist?("#{f}.pdf")
 
318
            i += 1
 
319
          else
 
320
            figname = f
 
321
            break
 
322
          end
 
323
          prev = f
 
324
        end
 
325
      end
 
326
      
 
327
      info { "Producing figure '#{figname}'" }
 
328
 
 
329
      t = create_figure_maker
 
330
      # If figname is clearly a path, we split it into directory/name
 
331
      # and set the output directory to directory.
 
332
      if File::basename(figname) != figname
 
333
        dir = File::dirname(figname)
 
334
        # If path is relative and output_directory is specified, we make
 
335
        # the path relative to output_dir
 
336
        if @output_directory && dir =~ /^[^\/~]/
 
337
          dir = File::join(@output_directory, dir)
 
338
        end
 
339
        t.save_dir = dir
 
340
        figname = File::basename(figname)
 
341
      elsif @output_directory
 
342
        t.save_dir = @output_directory
 
343
      end
 
344
 
 
345
      t.def_figure(figname) do
 
346
        @root_object.draw_root_object(t)
 
347
      end
 
348
      t.make_preview_pdf(t.figure_index(figname))
 
349
 
 
350
      file = t.save_dir ? File::join(t.save_dir, figname + ".pdf") : 
 
351
        figname + ".pdf"
 
352
 
 
353
      # Feed it
 
354
      @postprocess.process_file(file, last)
 
355
      return file
 
356
    end
 
357
 
 
358
    # Add *one* Data::Dataset object using the current style (that can
 
359
    # be overridden by stuff given as options) to the #root_object.
 
360
    #
 
361
    # \todo here, keep a state of the current styles:
 
362
    # * which is the color/marker/filling and so on of the curve ?
 
363
    # * are we drawing plain 2D curve, a histogram or something
 
364
    #   even more fancy ?
 
365
    # * this should be a separated class.
 
366
    #
 
367
    # \todo all curve objects should only take a Data::Dataset and a
 
368
    # style as arguments to new.
 
369
    def add_curve(dataset, options = {})
 
370
      plot = @root_object.current_plot
 
371
      curve = @curve_generator.
 
372
        curve_from_dataset(plot, dataset, options)
 
373
      plot.add_element(curve)
 
374
      @curve_style_stack << curve.curve_style
 
375
      info { "Adding curve '#{dataset.name}' to the current plot" }
 
376
    end
 
377
 
 
378
    # Transforms a _dataset_spec_ into one or several Data::Dataset
 
379
    # using the current backend (or any other that might be specified
 
380
    # in the options), and add them as curves to the #root_object,
 
381
    # using #add_curve
 
382
    def add_curves(dataset_spec, options = {})
 
383
      begin
 
384
        sets = @data_stack.get_datasets(dataset_spec, options)
 
385
      rescue Exception => exception
 
386
        error { "A problem occurred while processing dataset '#{dataset_spec}' using backend #{@data_stack.backend_factory.current.description.name}. Ignoring it." }
 
387
        debug { format_exception(exception) }
 
388
        return
 
389
      end
 
390
      for set in sets
 
391
        # We first trim elements from options that are not inside
 
392
        # Graphics::Styles::CurveStyleFactory::PlotCommandOptions
 
393
        options.delete_if { |k,v|
 
394
          ! Graphics::Styles::
 
395
          CurveStyleFactory::PlotCommandOptions.key?(k)
 
396
        }
 
397
        add_curve(set, options)
 
398
      end
 
399
    end
 
400
 
 
401
    protected
 
402
 
 
403
    # Creates a new FigureMaker object and returns it
 
404
    def create_figure_maker
 
405
      t = Tioga::FigureMaker.new
 
406
      t.tex_preamble += @latex_preamble
 
407
      t.autocleanup = @cleanup
 
408
 
 
409
      # The title field of the information is the command-line if marking
 
410
      # is on.
 
411
      if @mark
 
412
        title = "/Title (#{Utils::pdftex_quote_string(quoted_command_line)})\n"
 
413
      else
 
414
        title = ""
 
415
      end
 
416
 
 
417
      # We use Vincent's algorithm for major ticks when available ;-)...
 
418
      begin
 
419
        t.vincent_or_bill = true
 
420
        info { "Using Vincent's algorithm for major ticks" }
 
421
      rescue
 
422
        info { "Using Bill's algorithm for major ticks" }
 
423
      end
 
424
 
 
425
      
 
426
      # We now use \pdfinfo to provide information about the version
 
427
      # of ctioga2 used to produce the PDF, and the command-line if
 
428
      # applicable.
 
429
      t.tex_preamble += 
 
430
        "\n\\pdfinfo {\n#{title}/Creator(#{Utils::pdftex_quote_string("ctioga2 #{Version::version}")})\n}\n"
 
431
      return t
 
432
    end
 
433
 
 
434
 
 
435
    PlotGroup = CmdGroup.new('plots', "Plots","Plots",  0)
 
436
 
 
437
    PlotOptions = 
 
438
      Graphics::Styles::CurveStyleFactory::PlotCommandOptions.dup
 
439
    
 
440
 
 
441
    PlotOptions.merge!(Data::LoadDatasetOptions) do |key, oldval, newval| 
 
442
      raise "Duplicated option between PlotCommandOptions and LoadDatasetOptions"
 
443
    end
 
444
 
 
445
    PlotCommand = 
 
446
      Cmd.new("plot",nil,"--plot", 
 
447
              [ CmdArg.new('dataset') ], 
 
448
              PlotOptions ) do |plotmaker, set, options|
 
449
      plotmaker.add_curves(set, options)
 
450
    end
 
451
    
 
452
    PlotCommand.describe("Plots the given datasets",
 
453
                         <<EOH, PlotGroup)
 
454
Use the current backend to load the given datasets onto the data stack
 
455
and plot them. It is a combination of the {command: load} and the
 
456
{command: plot-last} commands; you might want to see their
 
457
documentation.
 
458
EOH
 
459
 
 
460
    PlotLastOptions = 
 
461
      Graphics::Styles::CurveStyleFactory::PlotCommandOptions.dup
 
462
 
 
463
    PlotLastOptions['which'] = CmdArg.new('stored-dataset')
 
464
    
 
465
    PlotLastCommand = 
 
466
      Cmd.new("plot-last",'-p',"--plot-last", 
 
467
              [], PlotLastOptions) do |plotmaker, options|
 
468
      ds = plotmaker.data_stack.specified_dataset(options)
 
469
      options.delete('which')   # To avoid problems with extra options.
 
470
      plotmaker.add_curve(ds, options)
 
471
    end
 
472
    
 
473
    PlotLastCommand.describe("Plots the last dataset pushed onto the stack",
 
474
                             <<EOH, PlotGroup)
 
475
Plots the last dataset pushed onto the data stack (or the one
 
476
specified with the @which@ option), with the current style. All
 
477
aspects of the curve style (colors, markers, line styles...) can be
 
478
overridden through the use of options.
 
479
EOH
 
480
 
 
481
    LaTeXGroup = CmdGroup.new('latex', "LaTeX",<<EOD, 30)
 
482
Commands providing control over the LaTeX output (preamble,
 
483
packages...)
 
484
EOD
 
485
    
 
486
    UsePackageCommand = 
 
487
      Cmd.new("use",nil,"--use", 
 
488
              [ CmdArg.new('text') ],
 
489
              { 'arguments' => CmdArg.new('text')}
 
490
              ) do |plotmaker, package, options|
 
491
      if options['arguments']
 
492
        plotmaker.latex_preamble << 
 
493
          "\\usepackage[#{options['arguments']}]{#{package}}\n"
 
494
      else
 
495
        plotmaker.latex_preamble << "\\usepackage{#{package}}\n"
 
496
      end
 
497
    end
 
498
 
 
499
    UsePackageCommand.describe('Includes a LaTeX package',
 
500
                               <<EOD, LaTeXGroup)
 
501
Adds a command to include the LaTeX package into the preamble. The 
 
502
arguments, if given, are given within [square backets].
 
503
EOD
 
504
 
 
505
    PreambleCommand = 
 
506
      Cmd.new("preamble",nil,"--preamble", 
 
507
              [ CmdArg.new('text') ]) do |plotmaker, txt|
 
508
      plotmaker.latex_preamble << "#{txt}\n"
 
509
    end
 
510
 
 
511
    PreambleCommand.describe('Adds a string to the LaTeX preamble',
 
512
                             <<EOD, LaTeXGroup)
 
513
Adds the given string to the LaTeX preamble of the output.
 
514
EOD
 
515
 
 
516
    Utf8Command = 
 
517
      Cmd.new("utf8",nil,"--utf8", []) do |plotmaker|
 
518
      plotmaker.latex_preamble << 
 
519
        "\\usepackage[utf8]{inputenc}\n\\usepackage[T1]{fontenc}"
 
520
    end
 
521
 
 
522
    Utf8Command.describe('Uses UTF-8 in strings',
 
523
                         <<EOD, LaTeXGroup)
 
524
Makes ctioga2 use UTF-8 for all text. It is exactly equivalent to
 
525
the command {command: preamble} with the argument:
 
526
 
 
527
@ \\usepackage[utf8]{inputenc}\\usepackage[T1]{fontenc}
 
528
 
 
529
EOD
 
530
 
 
531
 
 
532
    
 
533
    OutputSetupGroup =  
 
534
      CmdGroup.new('output-setup', 
 
535
                   "Output setup", <<EOD, 50)
 
536
Commands in this group deal with various aspects of the production of
 
537
output files:
 
538
 * output file location
 
539
 * post-processing (including automatic display)
 
540
 * cleanup...
 
541
EOD
 
542
 
 
543
    PageSizeCommand = 
 
544
      Cmd.new("page-size",'-r',"--page-size", 
 
545
              [ CmdArg.new('text') ], # \todo change that !
 
546
              { 'count-legend' => CmdArg.new('boolean')}
 
547
              ) do |plotmaker, size, options|
 
548
      plotmaker.root_object.set_page_size(size)
 
549
      if options.key? 'count-legend'
 
550
        plotmaker.root_object.count_legend_in_page = 
 
551
          options['count-legend']
 
552
      end
 
553
    end
 
554
 
 
555
    PageSizeCommand.describe('Sets the page size', 
 
556
                             <<EOH, OutputSetupGroup)
 
557
Sets the size of the output PDF file, in real units. Takes arguments in the 
 
558
form of 12cm x 3in (spaces can be omitted).
 
559
EOH
 
560
 
 
561
    CleanupCommand = 
 
562
      Cmd.new("clean",nil,"--clean", 
 
563
              [ CmdArg.new('boolean') ]) do |plotmaker, cleanup|
 
564
      plotmaker.cleanup = cleanup
 
565
    end
 
566
 
 
567
 
 
568
    CleanupCommand.describe('Remove intermediate files', 
 
569
                            <<EOH, OutputSetupGroup)
 
570
When this is on (the default), ctioga2 automatically cleans up
 
571
intermediate files produced by Tioga. When LaTeX fails, it can be
 
572
useful to have a closer look at them, so disable it to be able to look
 
573
into them.
 
574
EOH
 
575
 
 
576
 
 
577
    NameCommand = 
 
578
      Cmd.new("name",'-n',"--name", 
 
579
              [ CmdArg.new('text', 'figure name') ]) do |plotmaker, name|
 
580
      plotmaker.figure_name = name
 
581
    end
 
582
 
 
583
 
 
584
    NameCommand.describe('Sets the name of the figure', 
 
585
                         <<EOH, OutputSetupGroup)
 
586
Sets the name of the figure, which is also the base name for the output file.
 
587
This has nothing to do with the title of the plot, which can be set using
 
588
the command {command: title}.
 
589
 
 
590
If the name contains a %, it is interpreted by ctioga2 as a
 
591
printf-like format. It will attempt to find the first file that does
 
592
not exist, feeding it with increasing numbers.
 
593
 
 
594
The default value is now Plot-%03d, which means you'll get increasing numbers
 
595
automatically.
 
596
EOH
 
597
 
 
598
    OutputNowCommand = 
 
599
      Cmd.new("output-now",'-o',"--output", 
 
600
              [ CmdArg.new('text', 'figure name') ]) do |plotmaker, name|
 
601
      plotmaker.draw_figure(name)
 
602
    end
 
603
 
 
604
    OutputNowCommand.describe('Outputs the current state of the figure', 
 
605
                              <<EOH, OutputSetupGroup)
 
606
Writes a figure with the given name (see {command: name}) and keeps the 
 
607
current state. This can be used to create an animation.
 
608
EOH
 
609
 
 
610
    OutputAndResetCommand = 
 
611
      Cmd.new("output-and-reset",nil,"--output-and-reset", 
 
612
              [ ]) do |plotmaker|
 
613
      plotmaker.reset_graphics
 
614
    end
 
615
 
 
616
    OutputAndResetCommand.describe('Writes the current figure and starts anew', 
 
617
                                   <<EOH, OutputSetupGroup)
 
618
Writes the current figure and starts a fresh one. All non-graphical 
 
619
information are kept (curves loaded, figure names, preamble, and so on).
 
620
EOH
 
621
 
 
622
    OutputDirCommand = 
 
623
      Cmd.new("output-directory",'-O',"--output-directory", 
 
624
              [ CmdArg.new('text') ]) do |plotmaker, dir|
 
625
      plotmaker.output_directory = dir
 
626
    end
 
627
 
 
628
    OutputDirCommand.describe('Sets the output directory for produced files', 
 
629
                              <<EOH, OutputSetupGroup)
 
630
Sets the directory to which files will be plot. It defaults to the current
 
631
directory.
 
632
EOH
 
633
 
 
634
 
 
635
    # These commands belong rather to the PostProcess file, but, well,
 
636
    # they don't do much harm here anyway...
 
637
    
 
638
 
 
639
    ViewerCommand = 
 
640
      Cmd.new("viewer",nil,"--viewer", 
 
641
              [ CmdArg.new('text') ]) do |plotmaker, viewer|
 
642
      plotmaker.postprocess.viewer = viewer
 
643
    end
 
644
 
 
645
    ViewerCommand.describe('Uses the given viewer to view the produced PDF files', 
 
646
                           <<EOH, OutputSetupGroup)
 
647
Sets the command for viewing the PDF file after ctioga2 has been run.
 
648
EOH
 
649
    
 
650
    XpdfViewerCommand = 
 
651
      Cmd.new("xpdf",'-X',"--xpdf", [ ]) do |plotmaker|
 
652
      plotmaker.postprocess.viewer = "xpdf -z page"
 
653
    end
 
654
 
 
655
    XpdfViewerCommand.describe('Uses xpdf to view the produced PDF files', 
 
656
                              <<EOH, OutputSetupGroup)
 
657
Uses xpdf to view the PDF files produced by ctioga2.
 
658
EOH
 
659
 
 
660
    OpenViewerCommand = 
 
661
      Cmd.new("open",nil,"--open", [ ]) do |plotmaker|
 
662
      plotmaker.postprocess.viewer = "open"
 
663
    end
 
664
    
 
665
    OpenViewerCommand.describe('Uses open to view the produced PDF files', 
 
666
                               <<EOH, OutputSetupGroup)
 
667
Uses open (available on MacOS) to view the PDF files produced by ctioga2.
 
668
EOH
 
669
 
 
670
    SVGCommand = 
 
671
      Cmd.new("svg",nil,"--svg", 
 
672
              [CmdArg.new('boolean') ]) do |plotmaker,val|
 
673
      plotmaker.postprocess.svg = val
 
674
    end
 
675
    
 
676
    SVGCommand.describe('Converts produced PDF to SVG using pdf2svg', 
 
677
                        <<EOH, OutputSetupGroup)
 
678
When this feature is on, all produced PDF files are converted to SVG
 
679
using the neat pdf2svg program.
 
680
EOH
 
681
 
 
682
    EPSCommand = 
 
683
      Cmd.new("eps",nil,"--eps", 
 
684
              [CmdArg.new('boolean') ]) do |plotmaker,val|
 
685
      plotmaker.postprocess.eps = val
 
686
    end
 
687
    
 
688
    EPSCommand.describe('Converts produced PDF to EPS using pdftops', 
 
689
                        <<EOH, OutputSetupGroup)
 
690
When this feature is on, all produced PDF files are converted to EPS
 
691
using the pdftops program (from the xpdf tools suite).
 
692
EOH
 
693
 
 
694
    PNGCommand = 
 
695
      Cmd.new("png",nil,"--png", 
 
696
              [CmdArg.new('text', 'resolution') ],
 
697
              {
 
698
                'oversampling' => CmdArg.new('float'),
 
699
                'scale' => CmdArg.new('float'),
 
700
              }) do |plotmaker,res, opts|
 
701
      if res =~ /^\s*(\d+)\s*x\s*(\d+)\s*$/
 
702
        size = [$1.to_i, $2.to_i]
 
703
        plotmaker.postprocess.png_res = size
 
704
        if opts['oversampling']
 
705
          plotmaker.postprocess.png_oversampling = opts['oversampling']
 
706
        end
 
707
        scale = opts['scale'] || 1
 
708
        plotmaker.postprocess.png_scale = scale
 
709
        page_size = size.map { |n| (n/(1.0 *scale)).to_s + "bp" }.join('x')
 
710
        plotmaker.root_object.set_page_size(page_size)
 
711
      else
 
712
        raise "Invalid resolution for PNG output: #{res}"
 
713
      end
 
714
    end
 
715
    
 
716
    PNGCommand.describe('Converts produced PDF to PNG using convert', 
 
717
                        <<EOH, OutputSetupGroup)
 
718
Turns all produced PDF files into PNG images of the given resolution
 
719
using convert. This also has for effect to set the {command:
 
720
page-size} to the resolution divided by the 'scale' option in
 
721
Postscript points. By default, 2 pixels are rendered for 1 final to
 
722
produce a nicely antialiased image. Use the 'oversampling' option to
 
723
change that, in case the output looks too pixelized. This option only
 
724
affects conversion time.
 
725
EOH
 
726
 
 
727
    MarkCommand = 
 
728
      Cmd.new("mark",nil,"--mark", 
 
729
              [CmdArg.new('boolean') ]) do |plotmaker,val|
 
730
      plotmaker.mark = val
 
731
    end
 
732
    
 
733
    MarkCommand.describe('Fills the title of the produced PDF with the command-line', 
 
734
                         <<EOH, OutputSetupGroup)
 
735
When this feature is on (which is the default, as it comes in very
 
736
useful), the 'title' field of the PDF informations is set to the
 
737
command-line that resulted in the PDF file. Disable it if you don't
 
738
want any information to leak.
 
739
 
 
740
Please note that this will not log the values of the CTIOGA2_PRE and
 
741
CTIOGA2_POST variables, so you might still get a different output if
 
742
you make heavy use of those.
 
743
EOH
 
744
 
 
745
  end
 
746
 
 
747
end
 
748