class CTioga2::PlotMaker

This class is the core of ctioga. It parses the command-line arguments, reads all necessary files and plots graphs. Most of its functionality is delegated into classes.

todo An important point would be to provide a facility that holds all the default values. To each would be assigned a given name, and programs would only use something like code value = Default::value('stuff') endcode

Setting up defaults would only be a question of using one single command (with admittedly many optional arguments)

Constants

CleanupCommand
CleanupPDFCommand
DepsCommand
EPSCommand
LaTeXGroup
LatexFontCommand
MarkCommand
NameCommand
OpenViewerCommand
OutputAndResetCommand
OutputDirCommand
OutputNowCommand
OutputSetupGroup
PNGCommand
PageSizeCommand
PlotCommand
PlotGroup
PlotLastCommand
PlotLastOptions
PlotOptions
PreambleCommand
ResolutionCommand
SVGCommand
UsePackageCommand
Utf8Command
ViewerCommand

These commands belong rather to the PostProcess file, but, well, they don't do much harm here anyway…

XpdfViewerCommand

Attributes

cleanup[RW]

Whether intermediate files are cleaned up automatically afterwards or not…

curve_generator[RW]

A Graphics::CurveGenerator object in charge of producing suitable elements to be added to the Graphics::RootObject

curve_style_stack[RW]

The stack of CurveStyle objects that were used so far.

data_stack[RW]

The Data::DataStack object that manipulates Dataset objects

figure_name[RW]

The name of the figure

interpreter[RW]

The Commands::Interpreter object which runs all the commands.

latex_font[RW]

Global font information

latex_preamble[RW]

Additional preamble for LaTeX output

makefile_dependencies[RW]

If not empty, a Makefile file where the dependencies are written out

mark[RW]

Whether or not to include the command-line used to produce the file in the target PDF file.

output_directory[RW]

The output directory

pause_on_errors[RW]

Whether or not one should pause at the end if there are errors. Useful on windows, where the windows closes before one has a chance to see anything.

pdf_resolution[RW]

The resolution of the PDF file

postprocess[RW]

What happens to generated PDF files (a PostProcess object)

root_object[RW]

The Graphics::RootObject in charge of holding all things that will eventually get drawn

Public Class Methods

new() click to toggle source

Setting up of the PlotMaker object

# File lib/ctioga2/plotmaker.rb, line 253
def initialize
  CTioga2::Log::init_logger
  @data_stack = Data::DataStack.new
  @root_object = Graphics::RootObject.new
  @interpreter = Commands::Interpreter.new(self)
  @curve_generator = Graphics::CurveGenerator.new

  # Figure name:
  @figure_name = nil

  # Original preamble
  @latex_preamble = ""

  @latex_font = Graphics::Styles::FullLatexFont.new

  @latex_font.size = 15

  @postprocess = PostProcess.new

  # Make sure it is registered
  @@first_plotmaker_instance ||= self

  # We mark by default, as it comes dead useful.
  @mark = true

  # Remove intermediate files by default.
  @cleanup = true

  # Make curve style stack empty
  @curve_style_stack = []
end
plotmaker() click to toggle source

Returns the first created instance of PlotMaker. This sounds less object-oriented, yes, but that can come in useful some times.

# File lib/ctioga2/plotmaker.rb, line 247
def self.plotmaker
  return @@first_plotmaker_instance
end

Public Instance Methods

add_curve(dataset, options = {}) click to toggle source

Add one Data::Dataset object using the current style (that can be overridden by stuff given as options) to the root_object.

todo here, keep a state of the current styles:

  • which is the color/marker/filling and so on of the curve ?

  • are we drawing plain 2D curve, a histogram or something even more fancy ?

  • this should be a separated class.

todo all curve objects should only take a Data::Dataset and a style as arguments to new.

# File lib/ctioga2/plotmaker.rb, line 446
def add_curve(dataset, options = {})
  plot = @root_object.current_plot
  curve = @curve_generator.
    curve_from_dataset(plot, dataset, options)
  plot.add_element(curve)
  @curve_style_stack << curve.curve_style
  info { "Adding curve '#{dataset.name}' to the current plot" }
end
add_curves(dataset_spec, options = {}) click to toggle source

Transforms a dataset_spec into one or several Data::Dataset using the current backend (or any other that might be specified in the options), and add them as curves to the root_object, using add_curve

# File lib/ctioga2/plotmaker.rb, line 459
def add_curves(dataset_spec, options = {})
  begin
    sets = @data_stack.get_datasets(dataset_spec, options)
  rescue Exception => exception
    error { "A problem occurred while processing dataset '#{dataset_spec}' using backend #{@data_stack.backend_factory.current.description.name}. Ignoring it." }
    debug { format_exception(exception) }
    return
  end
  for set in sets
    add_curve(set, options)
  end
end
draw_figure(figname = "Plot-%03d", last = false) click to toggle source

Draws the figure currently accumulated in the root_object. It returns the path of the PDF file produced.

If figname contains a % sign, it will be interpreted as a format, and ctioga will attempt to find the first numbered file that does not exists.

todo

  • cleanup or not ?

# File lib/ctioga2/plotmaker.rb, line 351
def draw_figure(figname = "Plot-%03d", last = false)
  return if @root_object.empty?
  
  if figname =~ /%/
    i = 0
    prev = figname.dup
    while true
      f = figname % i
      if f == prev
        figname = f
        break
      end
      if File::exist?("#{f}.pdf")
        i += 1
      else
        figname = f
        break
      end
      prev = f
    end
  end
  
  info { "Producing figure '#{figname}'" }


  t = create_figure_maker

  path = Pathname.new(figname)
  if @output_directory
    out = Pathname.new(@output_directory)
    path = out + path
  end

  # We always cd into the target directory for creading the
  Utils::chdir(path.dirname) do
    fn = path.basename.to_s

    efn = fn.gsub(/[.\s]/) do |x|
      "__#{x[0].ord}__"
    end

    if efn != fn
      debug { "Mangled name to '#{efn}'"}
    end

    t.def_figure(efn) do
      @latex_font.set_global_font(t)
      @root_object.draw_root_object(t)
    end
    t.make_preview_pdf(t.figure_index(efn))

    # We look for latex errors
    if t.respond_to? :pdflatex_errors
      errs = t.pdflatex_errors
      if errs.size > 0
        error { "pdflatex returned with #{errs.size} error lines"}
        for l in errs
          warn { "pdflatex error: #{l.chomp}" }
        end
      end
    end

    # We first rename the PDF file if it was mangled.
    if efn != fn
      File::rename("#{efn}.pdf", "#{fn}.pdf")
    end

  end

  file = path.to_s + ".pdf"

  if @makefile_dependencies
    File.open(@makefile_dependencies, "w") do |f|
      deps = Utils::used_files.values
      # TODO: handle spaces
      f.puts "#{file}: #{deps.join(" ")}"
    end
  end

  # Feed it
  @postprocess.process_file(file, last)
  return file
end
quoted_command_line() click to toggle source

Returns a quoted version of the command line, that possibly could be used again to reproduce the same results.

# File lib/ctioga2/plotmaker.rb, line 334
def quoted_command_line
  quoted_args = @command_line.collect do |s|
    Utils::shell_quote_string(s)
  end.join ' '
  
  return "#{File.basename($0)} #{quoted_args}"
end
reset_graphics() click to toggle source

Flushes the current root object and starts a new one:

# File lib/ctioga2/plotmaker.rb, line 325
def reset_graphics
  draw_figure(@figure_name || "Plot-%03d", true)

  @root_object = Graphics::RootObject.new
  @curve_generator = Graphics::CurveGenerator.new
end
run(command_line) click to toggle source

ctioga's entry point.

Returns true if there was no errors and false if there was one or more.

# File lib/ctioga2/plotmaker.rb, line 289
def run(command_line)

  # The main catch-all around the plot:
  begin
    @command_line = command_line.dup
    if ENV.key? 'CTIOGA2_PRE'
      command_line.unshift(*Shellwords.shellwords(ENV['CTIOGA2_PRE']))
    end
    
    if ENV.key? 'CTIOGA2_POST'
      command_line.push(*Shellwords.shellwords(ENV['CTIOGA2_POST']))
    end
    
    @interpreter.run_command_line(command_line)
    
    # Now, draw the main figure
    file = draw_figure(@figure_name || "Plot-%03d", true)
  rescue SystemExit => e
    # We special-case the exit exception ;-)...
  rescue Exception => e
    debug { format_exception(e) }
    fatal { "#{e.message}" }
  end
  errs = Log.counts[:error]
  warns = Log.counts[:warn]
  if errs + warns > 0
    puts "ctioga2 finished with #{errs} errors and #{warns} warning"
    if @pause_on_errors
      puts "Hit ENTER to exit"
      STDIN.gets
    end
  end
  return errs == 0
end

Protected Instance Methods

create_figure_maker() click to toggle source

Creates a new FigureMaker object and returns it

# File lib/ctioga2/plotmaker.rb, line 475
    def create_figure_maker
      t = Tioga::FigureMaker.new(@pdf_resolution || 100)
      t.tex_preamble += @latex_preamble
      t.autocleanup = @cleanup

      # The title field of the information is the command-line if marking
      # is on.
      if @mark
        title = "/Title (#{Utils::pdftex_quote_string(quoted_command_line)})\n"
      else
        title = ""
      end

      # We use Vincent's algorithm for major ticks when available ;-)...
      begin
        t.vincent_or_bill = true
        info { "Using Vincent's algorithm for major ticks" }
      rescue
        info { "Using Bill's algorithm for major ticks" }
      end

      
      # We now use \pdfinfo to provide information about the version
      # of ctioga2 used to produce the PDF, and the command-line if
      # applicable.
      t.tex_preamble += 
        "\n\\pdfinfo {\n#{title}/Creator(#{Utils::pdftex_quote_string("ctioga2 #{Version::version}")})\n}\n" 

#" #emacs ruby-mode is a dummy
      return t
    end