split/Tioga/lib/FigMkr.rb in tioga-1.6 vs split/Tioga/lib/FigMkr.rb in tioga-1.7
- old
+ new
@@ -19,34 +19,34 @@
along with Tioga; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
=end
require 'Tioga/FigureConstants.rb'
+require 'Tioga/Utils.rb'
module Tioga
class FigureMaker
include FigureConstants
@@default_figure_maker = nil
@@which_pdflatex = nil
@@initialized = false # set true by the C code when first make a figure
-
- # The tag used for cvs export
- CVS_TAG = "rel_1_6" # now manually cheating...
-
- # Version now uses the CVS_TAG to create the version number. CVS_TAG should
- # look like 'rel_1_1_0' for the 1.1.0 release.
+ # This URL will contain tioga-(...) when it is exported from the
+ # SVN repository. This is where we'll look for version information.
+ SVN_URL = '$HeadURL: svn+ssh://rubyforge.org/var/svn/tioga/tags/tioga/Tioga%201.7/split/Tioga/lib/FigMkr.rb $'
+
+ TIOGA_VERSION = if SVN_URL =~ /tags\/tioga\/Tioga%20([^\/]+)/
+ $1
+ else
+ "SVN version"
+ end
+
+
def FigureMaker.version
- CVS_TAG =~ /\D+(.*?)\s*\$?$/
- version = $1.tr("-_", "..")
- if version.length > 0
- return version
- else
- return "SVN $Revision: 385 $" # Can't do better than that.
- end
+ TIOGA_VERSION
end
def FigureMaker.default
@@default_figure_maker = FigureMaker.new if @@default_figure_maker == nil
@@ -56,29 +56,45 @@
def FigureMaker.default=(fm)
@@default_figure_maker = fm
end
def FigureMaker.pdflatex
- @@which_pdflatex = 'pdflatex' if @@which_pdflatex == nil
- @@which_pdflatex
+ @@which_pdflatex = 'pdflatex' if @@which_pdflatex == nil
+ @@which_pdflatex
end
def FigureMaker.pdflatex=(s)
- @@which_pdflatex = s
+ @@which_pdflatex = s
end
def FigureMaker.make_name_lookup_hash(ary)
dict = Hash.new
ary.each { |name| dict[name] = true }
return dict
end
+
+
+ attr_accessor :title
+ attr_accessor :xlabel
+ attr_accessor :ylabel
+
+ attr_accessor :xaxis_locations_for_major_ticks
+ attr_accessor :xaxis_locations_for_minor_ticks
+ attr_accessor :xaxis_tick_labels
+
+ attr_accessor :yaxis_locations_for_major_ticks
+ attr_accessor :yaxis_locations_for_minor_ticks
+ attr_accessor :yaxis_tick_labels
+
attr_accessor :style_filename
attr_accessor :legend_info
+ attr_reader :line_type
+
attr_reader :num_figures
attr_reader :figure_names
attr_reader :figure_pdfs
@@ -118,13 +134,14 @@
# Whether or not do do automatic cleanup of the files
attr_accessor :autocleanup
# Whether or not do do multithreading for parallel pdflatex calls
attr_accessor :multithreads_okay_for_tioga
-
-
-
+
+ # An accessor for @measures_info:
+ attr_accessor :measures_info
+
# old preview attributes -- to be removed later
attr_accessor :model_number
attr_accessor :add_model_number
attr_accessor :need_to_reload_data
@@ -142,11 +159,10 @@
#attr_accessor :tex_preview_voffset
#attr_accessor :tex_preview_figure_width
#attr_accessor :tex_preview_figure_height
#attr_accessor :tex_preview_minwhitespace
#attr_accessor :tex_preview_fullpage
-
def reset_figures # set the state to default values
@figure_commands = []
@num_figures = 0
@create_save_dir = true # creates +save_dir+ by default
@@ -188,10 +204,12 @@
@tex_preview_figure_width = '\paperwidth - 2in'
@tex_preview_figure_height = '\paperheight - 2in'
@num_error_lines = 10
+ reset_plot_attrs
+
@tex_xoffset = 0
@tex_yoffset = 0
@tex_preview_hoffset = '1in'
@tex_preview_voffset = '1in'
@@ -250,18 +268,63 @@
# Automatic cleanup of by default
@autocleanup = true
# multithreads by default
@multithreads_okay_for_tioga = true
-
+
+
+ # The values of the sizes measured during the pdflatex run
+ # we need to keep track of them so we can decide how many times
+ # we'll run pdflatex.
+ @measures = {}
+
+ # We *must* initialize the measures_info hash.
+ @measures_info = {}
end
+
- def initialize
+ def reset_plot_attrs
+ @title = nil
+ @xlabel = nil
+ @ylabel = nil
+ @line_type = nil
+ @xaxis_locations_for_major_ticks = nil
+ @xaxis_locations_for_minor_ticks = nil
+ @xaxis_tick_labels = nil
+ @yaxis_locations_for_major_ticks = nil
+ @yaxis_locations_for_minor_ticks = nil
+ @yaxis_tick_labels = nil
+ private_init_fm_data
+ end
+
+
+ def initialize
+ @fm_data = Dvector.new(@@fm_data_size)
reset_figures
end
+ # Returns informations about the size of the named element.
+ # Returns a hash:
+ # * 'width' : the width of the box in figure coordinates
+ # * 'height': the height of the box in figure coordinates
+ # * 'just' : the justification used
+ # * 'align' : the vertical alignment used
+ # * 'angle' : the angle used
+ # * 'scale' : the *total* scale used.
+ #
+ # If the measurement did not take place yet, the width, height,
+ # and other attributes will be missing. Look for those.
+ def get_text_size(name, default = 1.0)
+ if self.measures_info.key? name
+ return self.measures_info[name]
+ else
+ return {} # Empty hash
+ end
+ end
+
+
def reset_state
reset_figures
end
@@ -349,10 +412,14 @@
def line_color=(color)
self.stroke_color=(color)
end
+ def line_type=(val)
+ self.line_type_set(val)
+ end
+
def stroke_width
self.line_width
end
def stroke_width=(width)
@@ -588,15 +655,16 @@
'x' => x, 'y' => y, 'scale' => self.legend_scale,
'justification' => self.legend_justification,
'alignment' => self.legend_alignment)
end
line_width = dict['line_width']
- if line_width >= 0
+ line_type = dict['line_type']
+ unless (line_width < 0) || ((line_type.kind_of?String) && (line_type.casecmp('none') == 0))
self.line_color = dict['line_color']
self.line_width = dict['line_width']
self.line_cap = dict['line_cap']
- self.line_type = dict['line_type']
+ self.line_type = line_type
stroke_line(line_x0, y+line_dy, line_x1, y+line_dy)
end
# place any marker right in the middle of the line
if dict['marker_dict'] != nil
marker_dict = dict['marker_dict']
@@ -872,12 +940,42 @@
return { 'top_margin' => top_margin, 'bottom_margin' => bottom_margin }
end
def context(&cmd)
- trace_cmd_no_arg(@enter_context_function, @exit_context_function) {
- private_context(cmd) }
+ trace_cmd_no_arg(@enter_context_function, @exit_context_function) {
+
+ save_title = self.title
+ save_xlabel = self.xlabel
+ save_ylabel = self.ylabel
+ save_line_type = self.line_type
+ save_xaxis_locations_for_major_ticks = self.xaxis_locations_for_major_ticks
+ save_xaxis_locations_for_minor_ticks = self.xaxis_locations_for_minor_ticks
+ save_xaxis_tick_labels = self.xaxis_tick_labels
+ save_yaxis_locations_for_major_ticks = self.yaxis_locations_for_major_ticks
+ save_yaxis_locations_for_minor_ticks = self.yaxis_locations_for_minor_ticks
+ save_yaxis_tick_labels = self.yaxis_tick_labels
+ save_fm_data = Dvector.new(@@fm_data_size).replace(@fm_data)
+ pdf_gsave
+ begin
+ cmd.call
+ ensure
+ pdf_grestore
+ self.title = save_title
+ self.xlabel = save_xlabel
+ self.ylabel = save_ylabel
+ self.line_type = save_line_type
+ self.xaxis_locations_for_major_ticks = save_xaxis_locations_for_major_ticks
+ self.xaxis_locations_for_minor_ticks = save_xaxis_locations_for_minor_ticks
+ self.xaxis_tick_labels = save_xaxis_tick_labels
+ self.yaxis_locations_for_major_ticks = save_yaxis_locations_for_major_ticks
+ self.yaxis_locations_for_minor_ticks = save_yaxis_locations_for_minor_ticks
+ self.yaxis_tick_labels = save_yaxis_tick_labels
+ @fm_data.replace(save_fm_data)
+ end
+
+ }
end
def rescale(factor)
rescale_text(factor)
@@ -1021,35 +1119,47 @@
check_dict(dict, @@keys_for_make_contour, 'make_contour')
z_level = dict['z_level']
if z_level == nil
z_level = complain_if_missing_numeric_arg(dict, 'z', 'level', 'make_contour')
end
- dest_xs = get_dvec(dict, 'dest_xs', 'make_contour')
- dest_ys = get_dvec(dict, 'dest_ys', 'make_contour')
+ dest_xs = dict['dest_xs']
+ dest_ys = dict['dest_ys']
xs = get_dvec(dict, 'xs', 'make_contour')
ys = get_dvec(dict, 'ys', 'make_contour')
gaps = dict['gaps']
if (!(gaps.kind_of? Array))
raise "Sorry: 'gaps' for 'make_contour' must be an Array"
end
zs = alt_names(dict, 'zs', 'data')
if (!(zs.kind_of? Dtable))
raise "Sorry: 'zs' for 'make_contour' must be a Dtable"
end
- dest_xs.clear; dest_ys.clear; gaps.clear
+ dest_xs.clear unless dest_xs == nil
+ dest_ys.clear unless dest_ys == nil
+ gaps.clear
legit = dict['legit']
if legit == nil
legit = Dtable.new(xs.length,ys.length).set(1.0)
elsif (!(legit.kind_of? Dtable))
raise "Sorry: 'legit' for 'make_contour' must be a Dtable -- nonzero means legitimate value in corresponding entry in zs"
end
method = dict['method']
use_conrec = (method == 'conrec' or method == 'CONREC')? 1 : 0
- private_make_contour(dest_xs, dest_ys, gaps, xs, ys, zs, z_level, legit, use_conrec)
+ pts_array = private_make_contour(gaps, xs, ys, zs, z_level, legit, use_conrec)
+ unless dest_xs == nil
+ dest_xs.resize(pts_array[0].size)
+ dest_xs.replace(pts_array[0])
+ end
+ unless dest_ys == nil
+ dest_ys.resize(pts_array[1].size)
+ dest_ys.replace(pts_array[1])
+ end
+ return pts_array
+
end
@@keys_for_make_steps = FigureMaker.make_name_lookup_hash([
'xfirst', 'x_first', 'yfirst', 'y_first', 'xlast', 'x_last', 'ylast', 'y_last',
'xs', 'ys', 'dest_xs', 'dest_ys'])
@@ -1057,28 +1167,42 @@
check_dict(dict, @@keys_for_make_steps, 'make_steps')
xfirst = complain_if_missing_numeric_arg(dict, 'xfirst', 'x_first', 'make_steps')
yfirst = complain_if_missing_numeric_arg(dict, 'yfirst', 'y_first', 'make_steps')
xlast = complain_if_missing_numeric_arg(dict, 'xlast', 'x_last', 'make_steps')
ylast = complain_if_missing_numeric_arg(dict, 'ylast', 'y_last', 'make_steps')
- dest_xs = get_dvec(dict, 'dest_xs', 'make_steps')
- dest_ys = get_dvec(dict, 'dest_ys', 'make_steps')
+ dest_xs = dict['dest_xs']
+ dest_ys = dict['dest_ys']
xs = get_dvec(dict, 'xs', 'make_steps')
ys = get_dvec(dict, 'ys', 'make_steps')
- private_make_steps(dest_xs, dest_ys, xs, ys, xfirst, yfirst, xlast, ylast)
+ pts_array = private_make_steps(xs, ys, xfirst, yfirst, xlast, ylast)
+ unless dest_xs == nil
+ dest_xs.resize(pts_array[0].size)
+ dest_xs.replace(pts_array[0])
+ end
+ unless dest_ys == nil
+ dest_ys.resize(pts_array[1].size)
+ dest_ys.replace(pts_array[1])
+ end
+ return pts_array
end
@@keys_for_make_curves = FigureMaker.make_name_lookup_hash([
'start_slope', 'end_slope', 'xs', 'ys', 'sample_xs', 'result_ys'])
def make_spline_interpolated_points(dict)
check_dict(dict, @@keys_for_make_curves, 'make_spline_interpolated_points')
start_slope = dict['start_slope']
end_slope = dict['end_slope']
sample_xs = get_dvec(dict, 'sample_xs', 'make_spline_interpolated_points')
- result_ys = get_dvec(dict, 'result_ys', 'make_spline_interpolated_points')
xs = get_dvec(dict, 'xs', 'make_spline_interpolated_points')
ys = get_dvec(dict, 'ys', 'make_spline_interpolated_points')
- private_make_spline_interpolated_points(sample_xs, result_ys, xs, ys, start_slope, end_slope)
+ yvec = private_make_spline_interpolated_points(sample_xs, xs, ys, start_slope, end_slope)
+ result_ys = dict['result_ys']
+ unless result_ys == nil
+ result_ys.resize(yvec.size)
+ result_ys.replace(yvec)
+ end
+ return yvec
end
@@keys_for_make_interpolant = FigureMaker.make_name_lookup_hash([
'start_slope', 'end_slope', 'xs', 'ys'])
def make_interpolant(dict)
@@ -1112,15 +1236,19 @@
raise "Sorry: Must give both 'x' and 'y' for show_error_bar."
end
dx = dict['dx']
dx_plus = get_if_given_else_default(dict, 'dx_plus', dx)
dx_minus = get_if_given_else_default(dict, 'dx_minus', dx)
+ dx_plus = 0 if dx_plus == nil
+ dx_minus = 0 if dx_minus == nil
dy = dict['dy']
dy_plus = get_if_given_else_default(dict, 'dy_plus', dy)
dy_minus = get_if_given_else_default(dict, 'dy_minus', dy)
- if (dx_plus == nil || dx_minus == nil || dy_plus == nil || dy_minus == nil)
- raise "Sorry: Must give both 'dx' and 'dy' error ranges for show_error_bar."
+ dy_plus = 0 if dy_plus == nil
+ dy_minus = 0 if dy_minus == nil
+ if (dx_plus == 0 && dy_minus == 0)
+ raise "Sorry: Must give either or both 'dx' and 'dy' error ranges for show_error_bar."
end
end_cap = get_if_given_else_default(dict, 'end_cap', 0.15) # end_cap length in default text heights
x_end_cap = end_cap * self.default_text_height_dy
y_end_cap = end_cap * self.default_text_height_dx
line_width = get_if_given_else_default(dict, 'line_width', 1)
@@ -1457,11 +1585,11 @@
return private_create_monochrome_image_data(data, first_row, last_row, first_column, last_column,
boundary, reverse);
end
@@keys_for_show_marker = FigureMaker.make_name_lookup_hash([
- 'marker', 'x', 'y', 'at', 'point', 'Xs', 'Ys', 'xs', 'ys', 'mode', 'rendering_mode',
+ 'marker', 'x', 'y', 'at', 'point', 'Xs', 'Ys', 'xs', 'ys', 'mode', 'rendering_mode', 'legend',
'angle', 'scale', 'font', 'string', 'text', 'color', 'fill_color', 'stroke_color', 'stroke_width',
'horizontal_scale', 'vertical_scale', 'italic_angle', 'ascent_angle', 'alignment', 'justification'])
def show_marker(dict)
check_dict(dict, @@keys_for_show_marker, 'show_marker')
marker = dict['marker']
@@ -1525,14 +1653,34 @@
h_scale = get_if_given_else_use_default_dict(dict, 'horizontal_scale', @marker_defaults)
v_scale = get_if_given_else_use_default_dict(dict, 'vertical_scale', @marker_defaults)
it_angle = get_if_given_else_use_default_dict(dict, 'italic_angle', @marker_defaults)
ascent_angle = get_if_given_else_use_default_dict(dict, 'ascent_angle', @marker_defaults)
glyph = 0 if glyph == nil
- int_args = glyph*100000 + font*1000 + mode*100 + align*10 + just
- # Ruby limits us to 15 args, so pack some small integers together
+ int_args = glyph*100000 + font*1000 + mode*100 + align*10 + (just+1) # min value for just is -1
+ # Ruby limits us to 15 args, so pack some small integers together
private_show_marker(int_args, stroke_width, string, x, y, xs, ys,
- h_scale, v_scale, scale, it_angle, ascent_angle, angle, fill_color, stroke_color)
+ h_scale, v_scale, scale, it_angle, ascent_angle, angle, fill_color, stroke_color)
+ legend_arg = dict['legend']
+ if legend_arg != nil
+ if legend_arg.kind_of?Hash
+ legend = legend_arg.dup
+ legend["line_type"] = 'None' if legend["line_type"] == nil
+ legend["marker"] = marker if legend["marker"] == nil
+ legend["marker_scale"] = scale if legend["marker_scale"] == nil
+ legend["marker_color"] = fill_color if legend["marker_color"] == nil
+ save_legend_info(legend)
+ elsif legend_arg.kind_of?String
+ save_legend_info(
+ 'line_type' => 'None',
+ 'marker' => marker,
+ 'marker_scale' => scale,
+ 'marker_color' => fill_color,
+ 'text' => legend_arg)
+ else
+ save_legend_info(legend_arg)
+ end
+ end
end
def show_label(dict)
at = alt_names(dict, 'at', 'point')
if check_pair(at, 'at', 'show_text')
@@ -1549,21 +1697,21 @@
show_text(dict)
end
@@keys_for_show_text = FigureMaker.make_name_lookup_hash([
'text', 'side', 'loc', 'position', 'pos', 'x', 'y',
- 'shift', 'scale', 'color', 'angle', 'alignment', 'justification', 'at', 'point'])
+ 'shift', 'scale', 'color', 'angle', 'alignment', 'justification', 'at', 'point', 'measure'])
def show_text(dict)
check_dict(dict, @@keys_for_show_text, 'show_text')
text = dict['text']
if text == nil
raise "Sorry: Must supply 'text' entry in dictionary for show_text"
end
scale = get_if_given_else_default(dict, 'scale', 1)
color = dict['color'] # color is [r,g,b] array. this adds \textcolor[rgb]{r,g,b}{...}
if color != nil
- if !color.kind_of?Array
+ if !color.kind_of? Array
raise "Sorry: 'color' must be array of [r,g,b] intensities for show_text (#{color})"
end
r = color[0]; g = color[1]; b = color[2];
if (!(r.kind_of? Numeric) || !(g.kind_of? Numeric) || !(b.kind_of? Numeric) )
raise "Sorry: 'color' must be array of [r,g,b] intensities for show_text"
@@ -1571,10 +1719,11 @@
text = sprintf("\\textcolor[rgb]{%0.2f,%0.2f,%0.2f}{%s}", r, g, b, text)
end
just = get_if_given_else_default(dict, 'justification', self.justification)
align = get_if_given_else_default(dict, 'alignment', self.alignment)
angle = get_if_given_else_default(dict, 'angle', 0)
+
loc = alt_names(dict, 'loc', 'side')
if (loc == nil)
at = alt_names(dict, 'at', 'point')
if check_pair(at, 'at', 'show_text')
xloc = at[0]
@@ -1584,11 +1733,12 @@
yloc = dict['y']
end
if (xloc == nil || yloc == nil)
raise "Sorry: Must supply a location for show_text"
end
- show_rotated_label(text, xloc, yloc, scale, angle, just, align)
+ show_rotated_label(text, xloc, yloc, scale, angle, just, align,
+ dict['measure'] || nil)
return
end
position = alt_names(dict, 'position', 'pos')
position = 0.5 if position == nil
shift = dict['shift']
@@ -1611,14 +1761,16 @@
yloc = shift*self.char_height_dy
xloc = convert_frame_to_figure_x(position)
else
raise "Sorry: 'loc' must be LEFT, RIGHT, TOP, BOTTOM, AT_X_ORIGIN, or AT_Y_ORIGIN for show_text"
end
- show_rotated_label(text, xloc, yloc, scale, angle, just, align)
+ show_rotated_label(text, xloc, yloc, scale, angle, just, align,
+ dict['measure'])
return
end
- show_rotated_text(text, loc, shift, position, scale, angle, just, align)
+ show_rotated_text(text, loc, shift, position, scale, angle, just,
+ align, dict['measure'])
end
def reset_eval_function
@eval_command = nil
@@ -1794,15 +1946,28 @@
def make_preview_pdf(num) # old name
make_pdf(num)
end
+ # We wrap the call so that if the keys of @measures
+ # did change during the call, we call it again.
def make_pdf(num) # returns pdf name if successful, false if failed.
+ old_measure_keys = @measures.keys
num = get_num_for_pdf(num)
result = start_making_pdf(num)
return unless result
- return @figure_pdfs[num] = finish_making_pdf(@figure_names[num])
+ begin
+ @figure_pdfs[num] = finish_making_pdf(@figure_names[num])
+ # If the keys have changed, we run that again.
+ rescue Exception => e
+ p e, e.backtrace
+ end
+
+ if @measures.keys != old_measure_keys
+ make_pdf(num)
+ end
+ return @figure_pdfs[num]
end
def require_pdf(num)
num = get_num_for_pdf(num)
@@ -1860,13 +2025,14 @@
end
return false if num == nil
cmd = @figure_commands[num]
return false unless cmd.kind_of?(Proc)
begin
+ reset_plot_attrs
reset_legend_info
- result = private_make(name, cmd)
- return result
+ private_make(name, cmd)
+ return true
rescue Exception => er
report_error(er, "ERROR while executing command: #{cmd}")
end
return false
end
@@ -1944,34 +2110,66 @@
end
end
def finish_making_pdf(name) # returns pdfname if succeeds, false if fails.
- pdflatex = FigureMaker.pdflatex
- quiet = @quiet_mode
- run_directory = @run_dir
- if (@save_dir == nil)
- syscmd = "#{pdflatex} -interaction nonstopmode #{name}.tex > pdflatex.log 2>&1"
- else
- syscmd = "cd #{@save_dir}; #{pdflatex} -interaction nonstopmode #{name}.tex > pdflatex.log 2>&1"
+ pdflatex = FigureMaker.pdflatex
+ quiet = @quiet_mode
+ run_directory = @run_dir
+
+ if (@save_dir == nil)
+ logname = "pdflatex.log"
+ else
+ logname = "#{@save_dir}/pdflatex.log"
+ end
+
+ if (@save_dir == nil)
+ syscmd = "#{pdflatex} -interaction nonstopmode #{name}.tex"
+ else
+ syscmd = "cd #{@save_dir}; #{pdflatex} -interaction nonstopmode #{name}.tex"
+ end
+ # Now fun begins:
+ # We use IO::popen for three reasons:
+ # * first, we want to be able to read back information from
+ # pdflatex (see \tiogameasure)
+ # * second, this way of doing should be portable to win, while
+ # the > trick might not be that much...
+ # * third, closing the standard input of pdflatex will remove
+ # painful bugs, when pdflatex gets interrupted but waits
+ # for an input for which it didn't prompt.
+
+ @measures = {}
+ IO::popen(syscmd, "r+") do |f|
+ f.close_write # We don't need that.
+ log = File.open(logname, "w")
+ for line in f
+ log.print line
+ if line =~ /^(.*)\[(\d)\]=(.+pt)/
+ n = $1
+ num = $2.to_i
+ dim = Utils::tex_dimension_to_bp($3)
+ @measures[n] ||= []
+ @measures[n][num] = dim
+ end
end
- result = system(syscmd)
+ end
+
+ # Now passing the saved measures to the C code.
+ for key, val in @measures
+ # p @fm_data
+ private_save_measure(key, *val)
+ end
+
+ result = $?
if !result
- if (@save_dir == nil)
- logname = "pdflatex.log"
- else
- logname = "#{@save_dir}/pdflatex.log"
- end
puts "ERROR: #{pdflatex} failed."
file = File.open(logname)
if file == nil
puts "cannot open #{logname}"
else
reporting = false; linecount = 0
file.each_line do |line|
- firstchar = line[0..0]
- comparison = (firstchar <=> '!')
- reporting = true if comparison == 0
+ reporting = true if line =~ /^!/
if reporting
puts line
linecount = linecount + 1
break if linecount == @num_error_lines
end