Website/slides/talks/2018-1-Tour-Mimir/data/CT-InPractice/plot.rb

214 lines
6.3 KiB
Ruby

$plot_terminal = "aqua"
$plot_suffix = nil;
$plot_auto_open = false;
$current_plot = nil;
def plot_output(output, settings = {})
case output
when :aqua then $plot_terminal = "aqua"; $plot_suffix = nil;
when :pdf then $plot_terminal = "pdf"; $plot_suffix = ".pdf"
when :png then $plot_terminal = "png"; $plot_suffix = ".png"
end
$plot_terminal_opts = settings
end
def plot_terminal(setting_overrides = {})
settings = $plot_terminal_opts.merge setting_overrides
$plot_terminal+(
if settings.size < 1 then "" else
" " + settings.to_a.flatten.join(" ")
end
)
end
$pretty_styles = [
{ :lt => "rgb \"#A00000\"",
# :fs => "rgb \"#A00000\"",
:lw => 2,
:pt => 1
},
{ :lt => "rgb \"#00A000\"",
# :fs => "rgb \"#00A000\"",
:lw => 2,
:pt => 6
},
{ :lt => "rgb \"#5060D0\"",
# :fs => "rgb \"#5060D0\"",
:lw => 2,
:pt => 2
},
{ :lt => "rgb \"#F25900\"",
# :fs => "rgb \"#F25900\"",
:lw => 2,
:pt => 9
}
];
def pretty_style(idx, opts = {})
opts = opts.clone;
$pretty_styles[idx].each { |k, v| opts[k] = v unless opts.has_key? k }
opts.map { |kv| kv.to_a.join(" ") unless kv[1].nil? }.compact.join(" ")
end
def pretty_plot(plot, opts = {})
# plot based on Brighten Godfrey's blog post:
# http://youinfinitesnake.blogspot.com/2011/02/attractive-scientific-plots-with.html
plot.terminal [
"pdf",
"font \"#{opts.fetch(:fontface, "Times-Roman")},#{opts.fetch(:fontsize, 10)}\"",
"linewidth #{opts.fetch(:linewidth, 4)} rounded",
"fontscale #{opts.fetch(:fontscale, 1.0)}",
"size #{opts.fetch(:sizex, 5)}in,#{opts.fetch(:sizey, 3)}in"
].join(" ")
# Line style for axes
plot.style "line 80 lc #{opts.fetch(:bordercolor, "rgb \"#808080\"")}"
# Line style for grid
plot.style "line 81 lt 0" # dashed
plot.style "line 81 lc #{opts.fetch(:gridcolor, "rgb \"#808080\"")}" # grey
plot.grid "back linestyle 81"
border_groups =
opts.fetch(:border, [:left, :bottom]).map do |b|
case b
when :bottom then 1
when :left then 2
when :top then 4
when :right then 8
when :all then 1+2+4+8
else raise "Invalid border type : #{b}"
end
end.sum
plot.border "#{border_groups} back linestyle 80" # Remove border on top and right. These
# borders are useless and make it harder
# to see plotted lines near the border.
# Also, put it in grey; no need for so much emphasis on a border.
plot.xtics "nomirror"
plot.ytics "nomirror"
if(opts.fetch(:logx, false)) then
plot.logscal "x"
plot.mxtics "10" # Makes logscale look good.
end
if(opts.fetch(:logy, false)) then
plot.logscal "y"
plot.mytics "10" # Makes logscale look good.
end
# Line styles: try to pick pleasing colors, rather
# than strictly primary colors or hard-to-see colors
# like gnuplot's default yellow. Make the lines thick
# so they're easy to see in small plots in papers.
$pretty_styles.each_index { |x| plot.style "line #{x+1} #{pretty_style(x)}" }
plot.key "bottom right"
end
def auto_open_plots(new_val = true)
$plot_auto_open = new_val;
end
def row_data(data)
$current_plot.data << Gnuplot::DataSet.new(data.unzip) { |ds| yield ds }
end
def plot(args = {})
task(args) do
task_name = case args
when Hash then args.keys[0]
when Symbol,String then args.to_s
end
Gnuplot.open do |gp|
Gnuplot::Plot.new(gp) do |plot|
$current_plot = plot;
plot.terminal plot_terminal
if $plot_suffix and task_name then
plot.output "#{task_name}#{$plot_suffix}"
end
yield plot;
end
end
if $plot_auto_open and [".pdf", ".png"].include? $plot_suffix
system("open #{task_name}#{$plot_suffix}")
end
end
end
def line_plot(args = {})
plot(args) do |plot|
data_elements = yield(plot)
data_elements = { :data => data_elements } unless data_elements.is_a? Hash;
data = data_elements[:data].unzip;
xaxis = data_elements.fetch(:xaxis) { data.shift };
keys = data_elements.fetch(:keys) { data.map { nil; } }
withs = data_elements.fetch(:with, "linespoints");
withs = data.map { withs } unless withs.is_a? Array;
raise "Missing data!" if data.nil?;
raise "Missing X Axis!" if xaxis.nil?;
data.zip(keys, withs).each do |line, key, with|
plot.data << Gnuplot::DataSet.new([xaxis, line]) do |ds|
ds.title = key unless key.nil?
ds.with = with unless with.nil?
end
end
end
end
def draw_clustered_bar_plot plot, args = {}
data = args.fetch(:data).unzip;
base_offset = args.fetch(:base_offset, 0);
interbar_offset = args.fetch(:interbar_offset, 18);
intergroup_offset = args.fetch(:intergroup_offset, interbar_offset);
margins = args.fetch(:margins, intergroup_offset);
bar_width = args.fetch(:bar_width, 10);
tic_commands = args.fetch(:tic_commands, "");
label_offset = args.fetch(:label_offset, 0);
box_style = args.fetch(:box_style,
lambda { |i| "boxes fill pattern #{i}" });
plot.grid "noxtics"
group_offset = base_offset + margins
group_size = interbar_offset * data.length + intergroup_offset;
plot.boxwidth bar_width.to_s;
pattern = 0;
data.zip(args[:dataset_labels]).each do |dataset, dataset_title|
offset = group_offset - group_size;
group_offset += interbar_offset;
indices = dataset.map { |i| offset += group_size; }
plot.data << Gnuplot::DataSet.new([indices,dataset]) do |ds|
ds.title = dataset_title
ds.with = box_style.call(pattern += 1);
end
end
label_offset += (group_size+intergroup_offset-margins)/2
group_offset = base_offset - label_offset;
plot.xtics "(#{args[:group_labels].map do |label|
"\"#{label}\" #{group_offset += group_size}";
end.join(", ")}) scale 0 #{tic_commands}";
plot.xrange "[-10:#{group_offset+label_offset+margins-intergroup_offset}]"
end
def draw_bar_plot plot, args
plot.key "off"
args = args.clone
args[:data] = args[:data].map {|d| [d]}
args[:dataset_labels] = [""];
args[:group_labels] = args[:labels];
draw_clustered_bar_plot plot, args
end