########################### # Graph and Film Methods ########################## # class CodeRunner # Parse graphkit shorthand for a general graph and return it as [axes, options]. GraphKit shorthand is: # '<axis1>[ : <axis2> [ : <axis3 [ : <axis4> ] ] ] [ ; <graph_options> [ ; <conditions> [ ; <sort> ] ] ]' # i.e. all commands for the graph in one string def graphkit_shorthand(*gr) return gr if gr.size > 1 gr = gr[0] return gr if gr.class == Array axes, options, cons, srt = gr.split(/;/) axes = eval("['#{axes.split(/(?<!\\|:):(?!:)/).map{|s| s.gsub(/\\:/, ':')}.join("','")}']") options = (options and options =~ /\S/) ? eval(options): {} # p srt cons = nil unless cons and cons=~ /\S/ srt = nil unless srt and srt=~ /\S/ options[:conditions] ||= cons options[:sort] ||= srt return [axes, options] end def sweep_graphkits(sweeps, options, &block) server = @server; @server = false intial_conditions = @conditions initial_sort = @sort @conditions = (options[:conditions] or @conditions) @sort = (options[:sort] or @sort) sweeps = sweeps.class == Array ? sweeps : [sweeps] generate_combined_ids # ep sweeps sweep_values = filtered_ids.inject([]) do |arr, id| arr.push(sweeps.inject([]) do |sweep_arr, var| sweep_arr.push @combined_run_list[id].send(var) # ep sweep_arr, var sweep_arr end) arr.uniq end old_conditions = @conditions new_options = options.dup new_options.delete(:sweep) if new_options[:sweep] # p new_options; gets kits = sweep_values.sort.map do |values| new_cons = (sweeps.zip(values).map do |name, value| "#{name}==#{value}" end).join(" and ") new_options[:conditions] = old_conditions ? old_conditions + " and #{new_cons}" : new_cons # ep new_options[:conditions] kit = yield(new_options) kit.data[0].gp.title = new_cons.gsub(/==/, '=') # p kit kit end # @conditions = old_conditions @conditions = intial_conditions @sort = initial_sort if options[:sweep_multid] kitdata = kits.map do |kit| kit.data[0].axes.values.map{|axiskit| axiskit.data} end final_axes = (kitdata[0].size + sweeps.size - 1).times.inject([]){|arr, i| arr.push []} data_hash = {} sweep_values.sort.each_with_index do |vals, i| # ep 'v', vals data = kitdata[i] length = data[0].size vals.each_with_index do |val, j| final_axes[j].push val # final_axes[j].uniq! end function = data.pop for j in 0...length data.each_with_index do |axisdata, k| # ep 'vk', vals.size + k # break if k == data.size - 1 final_axes[vals.size + k].push axisdata[j] final_axes[vals.size + k].uniq! end # ep 'h', final_axes, function data_hash[vals + data.map{|axisdata| axisdata[j]}] = function[j] end end final_axes.map!{|vals| vals.uniq.sort} final_data = SparseTensor.new(final_axes.size) data_hash.each do |coordinates, function| indices = [] coordinates.each_with_index do |val, i| indices[i] = final_axes[i].index(val) end final_data[*indices] = function end hash = {} # pp final_axes, final_data titles = sweeps + kits[0].data[0].axes.values.map{|axiskit| axiskit[:title]} (final_axes + [final_data]).each_with_index do |d, i| hash[GraphKit::AXES[i]] = {data: d, title: titles[i].to_s} end kit = GraphKit.autocreate(hash) # length = data[i].size # array = vals.map{|val| [val] * length} + data # p array # end # final_data = [[]] * (kitdata[0].size + sweeps.size) # sweep_values.sort.each_with_index do |vals, i| # data = kitdata[0] # length = data[0].size # array = vals.map{|val| [val] * length} + data # p array # end else kit = kits.inject{|ans, kit| ans + kit} end #Kernel.puts server_dump(kit) if server return kit end def sweep_graphkits(sweeps, options, &block) server = @server; @server = false intial_conditions = @conditions initial_sort = @sort @conditions = (options[:conditions] or @conditions) @sort = (options[:sort] or @sort) sweeps = sweeps.class == Array ? sweeps : [sweeps] generate_combined_ids # ep sweeps sweep_values = filtered_ids.inject([]) do |arr, id| arr.push(sweeps.inject([]) do |sweep_arr, sweep| sweep_arr.push @combined_run_list[id].instance_eval(sweep.to_s) # ep sweep_arr, var sweep_arr end) arr.uniq end old_conditions = @conditions new_options = options.dup new_options.delete(:sweep) if new_options[:sweep] # p new_options; gets kits = sweep_values.sort.map do |values| new_cons = (sweeps.zip(values).map do |name, value| "#{name}==#{value.inspect}" end).join(" and ") new_options[:conditions] = old_conditions ? old_conditions + " and #{new_cons}" : new_cons # ep new_options[:conditions] kit = yield(new_options) kit.data[0].gp.title = new_cons.gsub(/==/, '=') # p kit kit end # @conditions = old_conditions @conditions = intial_conditions @sort = initial_sort if options[:sweep_multid] kitdata = kits.map do |kit| kit.data[0].axes.values.map{|axiskit| axiskit.data} end final_axes = (kitdata[0].size + sweeps.size - 1).times.inject([]){|arr, i| arr.push []} data_hash = {} sweep_values.sort.each_with_index do |vals, i| # ep 'v', vals data = kitdata[i] length = data[0].size vals.each_with_index do |val, j| final_axes[j].push val # final_axes[j].uniq! end function = data.pop for j in 0...length data.each_with_index do |axisdata, k| # ep 'vk', vals.size + k # break if k == data.size - 1 final_axes[vals.size + k].push axisdata[j] final_axes[vals.size + k].uniq! end # ep 'h', final_axes, function data_hash[vals + data.map{|axisdata| axisdata[j]}] = function[j] end end final_axes.map!{|vals| vals.nuniq.sort} # ep final_axes final_data = SparseTensor.new(final_axes.size) data_hash.each do |coordinates, function| indices = [] # p coordinates coordinates.each_with_index do |val, i| indices[i] = final_axes[i].index(val) end final_data[*indices] = function end hash = {} # pp final_axes, final_data titles = sweeps + kits[0].data[0].axes.values.map{|axiskit| axiskit[:title]} (final_axes + [final_data]).each_with_index do |d, i| hash[GraphKit::AXES[i]] = {data: d, title: titles[i].to_s} end kit = GraphKit.autocreate(hash) # length = data[i].size # array = vals.map{|val| [val] * length} + data # p array # end # final_data = [[]] * (kitdata[0].size + sweeps.size) # sweep_values.sort.each_with_index do |vals, i| # data = kitdata[0] # length = data[0].size # array = vals.map{|val| [val] * length} + data # p array # end else kit = kits.inject{|ans, kit| ans + kit} end #Kernel.puts server_dump(kit) if server return kit end # call-seq: graphkit(axes, options) # Make a general graphkit, i.e. a graph that combines output from lots of runs. The axes should be an array of strings, each string defining what should be plotted on each axis. Each string is actually a fragment of Ruby code which will be evaluated by each run for whom option[:conditions] or @conditions evaulates to true. The most important other option is options[:sweep]. This is a string or an array of strings, each of which will be evaluated by each run; the data will then be grouped for the values of these strings. # E.g. # graphkit(['a', 'b'], {sweep: ["c"], conditions: 'd==1'} # def graphkit(*args) logf(:graphkit) if args.size == 1 axes, options = graphkit_shorthand(args[0]) elsif args.size == 2 axes, options = args else raise ArgumentError end # p axes # return named_graphkit(axes, options) if axes.class == String or axes.class == Symbol # ep @sort (return sweep_graphkits(options[:sweep], options) do |new_options| graphkit(axes, new_options) end) if options [:sweep] intial_conditions = @conditions initial_sort = @sort @conditions = (options[:conditions] or @conditions) @sort = (options[:sort] or @sort) sort_runs case axes.size when 1 kit = GraphKit.autocreate({x: axiskit(axes[0])}) kit.data[0].gp.with = "linespoints" when 2 kit = GraphKit.autocreate({x: axiskit(axes[0]), y: axiskit(axes[1])}) kit.data[0].gp.with = "linespoints" when 3 kit = GraphKit.autocreate({x: axiskit(axes[0]), y: axiskit(axes[1]), z: axiskit(axes[2])}) kit.data[0].gp.with = "linespoints" when 4 kit = GraphKit.autocreate({x: axiskit(axes[0]), y: axiskit(axes[1]), z: axiskit(axes[2]), f: axiskit(axes[3])}) kit.data[0].gp.with = "linespoints palette" else raise CRError.new("Bad graph string") end kit.title += %| for #{@conditions.gsub(/==/, "=").gsub(/\&\&/, " and ").gsub(/\|\|/, " or ").gsub(/\A(.{,40}).*\s/m, '\1')}| if @conditions kit.file_name = kit.title.gsub(/=/, ' eq ').gsub(/\&\&/, " and ").gsub(/\|\|/, " or ").gsub(/ /, "_").gsub(/\//, "over").gsub(/\*/, '.') kit.modify(options) # p kit kit.data[0].gp.title = "#@code:#{kit.data[0].title}" #Kernel.puts server_dump(kit) if @server return kit end # def gnuplot(axes, options) # a = graphkit(axes, options).gnuplot # end def axiskit(string) generate_combined_ids data = filtered_ids.inject([]) do |arr, id| begin arr.push @combined_run_list[id].instance_eval(string) rescue => err eputs "Evaluation of #{string} failed for run #{id}" raise err end end # p data return GraphKit::AxisKit.new(title: string, data: data) end def run_graphkit_shorthand(*grs) # p grs return grs if grs.size > 1 gr = grs[0] # p gr return gr if gr.class == Array name, options, cons, srt = gr.split(/;/) options = (options and options =~ /\S/) ? eval(options): {} cons = nil unless cons and cons=~ /\S/ srt = nil unless srt and srt=~ /\S/ options[:conditions] ||= cons if cons options[:sort] ||= srt if srt [name, options] end def run_graphkit(*args) if args.size == 1 name, options = run_graphkit_shorthand(args[0]) elsif args.size == 2 name, options = args else raise ArgumentError end (return sweep_graphkits(options[:sweep], options) do |new_options| run_graphkit(name, new_options) end) if options [:sweep] old_sort, old_conditions = @sort, @conditions @sort = options[:sort] if options[:sort] @conditions = options[:conditions] if options[:conditions] generate_combined_ids fids = filtered_ids raise CRError.new("No ids match these conditions: #@conditions") unless fids.size > 0 kit = (fids.map do |id| run = @combined_run_list[id]; # p run; STDIN.gets kit = run.graphkit(name, options.dup); kit.data[0].title ||= run.run_name; kit end).inject{|kit, k| kit+k} @sort, @conditions = old_sort, old_conditions #Kernel.puts server_dump(kit) if @server kit end def graphkit_from_lists(graphs, run_graphs, extra_options = {}) run_kits = run_graphs.map do |gr| name, options = run_graphkit_shorthand(gr) options += extra_options run_graphkit(name, options.dup) end kits = graphs.map do |gr| axes, options = graphkit_shorthand(gr) options += extra_options graphkit(axes, options.dup) end (run_kits + kits).inject{|kit, k| kit+k} end def graphkit_from_lists_with_frame_array(frame_array, graphs, run_graphs, extra_options = {}) server = @server; @server = false # ep "Frame array to calculate is", frame_array, "\n\n" i=0 array = frame_array.map do |frame_index| # if print_message eputs "\033[2A" # Terminal jargon - go back one line eputs sprintf("Fetching graphs: %2.2f", i.to_f/frame_array.size.to_f * 100.0) + "% Complete" # end i+=1 [frame_index, graphkit_from_lists(graphs, run_graphs, extra_options.absorb({(extra_options[:in]||extra_options[:index_name]||:frame_index) => frame_index}))] end # eputs Marshal.load(Marshal.dump(array)).pretty_inspect #Kernel.puts server_dump(array) if server return array end def film_graphkit(axes, options, film_options = {}) # self.class.make_film_multiple_runners([[self,[[[axes, options]],[]]]], film_options) film_from_lists([[axes, options]], [], film_options) end def film_run_graphkit(name, options, film_options = {}) # self.class.make_film_multiple_runners([[self,[[],[[name, options]]]]], film_options) film_from_lists([], [[name, options]], film_options) end def make_film_from_lists(graphs, run_graphs, film_options = {}) self.class.make_film_multiple_runners([[self,[graphs, run_graphs]]], film_options) end # list is an array of [[runner, [graphs, run_graphs]], ... ] def self.graphkit_multiple_runners(list, options={}) return list.inject(nil) do |kit, (runner, graph_lists)| graphs, run_graphs = graph_lists graphs.map!{|graph| runner.graphkit_shorthand(graph)} run_graphs.map!{|graph| runner.run_graphkit_shorthand(graph)} newkit = runner.graphkit_from_lists(graphs, run_graphs, options) kit ? kit + newkit : newkit end end def self.graphkit_multiple_runners_with_frame_array(frame_array, list, extra_options, print_message = false) i = 0 # return list.inject(nil) do |kit_array, (runner, graph_lists)| graphs, run_graphs = graph_lists graphs.map!{|graph| runner.graphkit_shorthand(graph)} run_graphs.map!{|graph| runner.run_graphkit_shorthand(graph)} newkit_array = runner.graphkit_from_lists_with_frame_array(frame_array, graphs, run_graphs, extra_options) # eputs newkit_array.pretty_inspect kit_array ? (i=-1; kit.map{|(frame_index, kit)| i+= 1;[frame_index, new_kit[i][1]]}) : newkit_array end end def self.make_film_multiple_runners_old(list, options) possible_options = [:frame_array, :fa, :skip_frames, :sf, :normalize, :n, :normalize_pieces, :np, :increment, :i, :skip_encoding, :se, :index_name, :in] fa = (options[:frame_array] or options[:fa] or list[0][0].run_list[list[0][0].filtered_ids[0]].frame_array(options) ) iname = options[:in]||options[:index_name]||:frame_index fd = frame_digits = Math.log10(fa[1]).ceil unless options[:skip_frames] or options[:sf] `rm -rf film_frames` extension = (options[:extension] or options[:ext] or '.png') extension = '.' + extension unless extension =~ /^\./ FileUtils.makedirs('film_frames') # puts @@multiple_processes; gets no_forks = (@@multiple_processes or 1) ep @@multiple_processes, no_forks end_graphkit = graphkit_multiple_runners(list, iname => fa[1]) begin_graphkit = graphkit_multiple_runners(list, iname => fa[0]) end_area = end_graphkit.plot_area_size begin_area = begin_graphkit.plot_area_size # p end_area, begin_area, options plot_size = {} axes = [:x, :y, :z] options[:normalize] ||= options[:nm] options[:normalize_pieces] ||= options[:nmp] for i in 0...end_area.size next unless options[:normalize] and options[:normalize].include? axes[i] min = [end_area[i][0], begin_area[i][0]].min max = [end_area[i][1], begin_area[i][1]].max key = axes[i] plot_size[key + :range] = [min, max] end ep plot_size # exit frames = [] actual_frames = {} i = fa[0] j = 0 while i <= fa[1] frames.push i actual_frames[i] = j i += (options[:ic] or options[:increment] or 1) j += 1 end frames.pieces(no_forks).each_with_index do |piece, myrank| fork do if options[:normalize_pieces] end_area = graphkit_multiple_runners(list, iname => piece.max).plot_area_size begin_area = graphkit_multiple_runners(list, iname => piece.min).plot_area_size axes = [:x, :y, :z] for i in 0...end_area.size next unless options[:normalize_pieces].include? axes[i] min = [end_area[i][0], begin_area[i][0]].min max = [end_area[i][1], begin_area[i][1]].max key = axes[i] plot_size[key + :range] = [min, max] end end eputs 'making graphs...'; sleep 1 graph_array = graphkit_multiple_runners_with_frame_array(piece, list, {:in => iname}, myrank==0) # ep graph_array eputs graph_array.each_with_index do |(frame_index,g), pindex| if myrank == 0 eputs "\033[2A" # Terminal jargon - go back one line eputs sprintf("Plotting graphs: %2.2f", pindex.to_f/piece.size.to_f * 100.0) + "% Complete" end # g = graph_kit_multiple_runners(list, plot_size + {frame_index: frame_index}) g.modify(plot_size) g.modify(options) # p g; exit g.title += sprintf(", frame %0#{fd}d", frame_index) unless options[:frame_title] == false folder = ("film_frames/"); file_name = sprintf("frame_%0#{fd}d", actual_frames[frame_index]) # g.gnuplot; gets; g.close # ep folder + file_name + '.png'; gets g.gnuplot_write(folder + file_name + extension) end end end eputs "Waiting on subprocesses..." Process.waitall end unless options[:skip_encoding] eputs "making film" frame_rate = (options[:frame_rate] or options[:fr] || 15) film_name = (options[:film_name] or options [:fn] or end_graphkit.file_name + '_film').gsub(/\s/, '_') puts `ffmpeg -y #{options[:bitrate] ? "-b #{options[:bitrate]}" : ""} -r #{frame_rate} -threads #{(@multiple_processes or 1)} -i film_frames/frame_%0#{fd}d#{extension} -qscale 0 #{film_name}.mp4` end end # end # __END__ def self.make_film_multiple_runners(list, options) possible_options = [:frame_array, :fa, :skip_frames, :sf, :normalize, :n, :normalize_pieces, :np, :increment, :i, :skip_encoding, :se] fa = (options[:frame_array] or options[:fa] or list[0][0].run_list[list[0][0].filtered_ids[0]].frame_array(options) ) iname = options[:in]||options[:index_name]||:frame_index fd = frame_digits = Math.log10(fa[1]).ceil unless options[:skip_frames] or options[:sf] # `rm -rf film_frames` # extension = (options[:extension] or options[:ext] or '.png') # extension = '.' + extension unless extension =~ /^\./ # FileUtils.makedirs('film_frames') # puts @@multiple_processes; gets no_forks = (@@multiple_processes or 1) ep @@multiple_processes, no_forks end_graphkit = graphkit_multiple_runners(list, iname => fa[1]) begin_graphkit = graphkit_multiple_runners(list, iname => fa[0]) end_area = end_graphkit.plot_area_size begin_area = begin_graphkit.plot_area_size # p end_area, begin_area, options plot_size = {} axes = [:x, :y, :z] options[:normalize] ||= options[:nm] options[:normalize_pieces] ||= options[:nmp] for i in 0...end_area.size next unless options[:normalize] and options[:normalize].include? axes[i] min = [end_area[i][0], begin_area[i][0]].min max = [end_area[i][1], begin_area[i][1]].max key = axes[i] plot_size[key + :range] = [min, max] end ep plot_size # exit frames = [] actual_frames = {} i = fa[0] j = 0 while i <= fa[1] frames.push i actual_frames[i] = j i += (options[:ic] or options[:increment] or 1) j += 1 end # graphkit_frame_array = [] myrank = -1 # graphkit_frame_array = (frames.pieces(no_forks).parallel_map(n_procs: no_forks, with_rank: true) do |piece, myrank| graphkit_frame_array = (frames.pieces(no_forks).map do |piece| myrank +=1 # ep 'myrank is', myrank # unless myrank==0 # # $stdout = $stderr = StringIO.new # end # fork do if options[:normalize_pieces] end_area = graphkit_multiple_runners(list, iname => piece.max).plot_area_size begin_area = graphkit_multiple_runners(list, iname => piece.min).plot_area_size axes = [:x, :y, :z] for i in 0...end_area.size next unless options[:normalize_pieces].include? axes[i] min = [end_area[i][0], begin_area[i][0]].min max = [end_area[i][1], begin_area[i][1]].max key = axes[i] plot_size[key + :range] = [min, max] end end eputs 'making graphs...'; sleep 1 if myrank==0 graph_array = graphkit_multiple_runners_with_frame_array(piece, list, {:in => iname}, myrank==0) # ep graph_array eputs graph_array.each_with_index do |(frame_index,g), pindex| # if myrank == 0 # eputs "\033[2A" # Terminal jargon - go back one line # eputs sprintf("Plotting graphs: %2.2f", pindex.to_f/piece.size.to_f * 100.0) + "% Complete" # end # g = graph_kit_multiple_runners(list, plot_size + {frame_index: frame_index}) g.modify(plot_size) g.modify(options) g.instance_eval options[:graphkit_modify] if options[:graphkit_modify] # p g; exit g.title += sprintf(", frame %0#{fd}d", frame_index) unless options[:frame_title] == false # folder = ("film_frames/"); # file_name = sprintf("frame_%0#{fd}d", actual_frames[frame_index]) # g.gnuplot; gets; g.close # ep folder + file_name + '.png'; gets # g.gnuplot_write(folder + file_name + extension) end graph_array # end end).sum # eputs "Waiting on subprocesses..." # Process.waitall end film_graphkit_frame_array(graphkit_frame_array, options) # unless options[:skip_encoding] # eputs "making film" # frame_rate = (options[:frame_rate] or options[:fr] || 15) # film_name = (options[:film_name] or options [:fn] or end_graphkit.file_name + '_film').gsub(/\s/, '_') # puts `ffmpeg -y #{options[:bitrate] ? "-b #{options[:bitrate]}" : ""} -r #{frame_rate} -threads #{(@multiple_processes or 1)} -i film_frames/frame_%0#{fd}d#{extension} -sameq #{film_name}.mp4` # end end def self.film_graphkit_frame_array(graphkit_frame_array, options) possible_options = [:frame_array, :fa, :skip_frames, :sf, :normalize, :n, :normalize_pieces, :np, :increment, :i, :skip_encoding, :se, :frame_rate, :fr, :size] # fa = (options[:frame_array] or options[:fa] or list[0][0].run_list[list[0][0].filtered_ids[0]].frame_array(options) ) fd = frame_digits = options[:fd]||Math.log10(graphkit_frame_array.map{|f, g| f}.max).ceil extension = (options[:extension] or options[:ext] or '.png') extension = '.' + extension unless extension =~ /^\./ unless options[:skip_frames] or options[:sf] FileUtils.rm_r('film_frames') if FileTest.exist?('film_frames') FileUtils.makedirs('film_frames') # puts @@multiple_processes; gets no_forks = (@@multiple_processes or 1) ep @@multiple_processes, no_forks # end_graphkit = graphkit_multiple_runners(list, frame_index: fa[1]) # begin_graphkit = graphkit_multiple_runners(list, frame_index: fa[0]) # end_area = end_graphkit.plot_area_size # begin_area = begin_graphkit.plot_area_size # p end_area, begin_area, options # plot_size = {} # axes = [:x, :y, :z] # options[:normalize] ||= options[:nm] # options[:normalize_pieces] ||= options[:nmp] # for i in 0...end_area.size # next unless options[:normalize] and options[:normalize].include? axes[i] # min = [end_area[i][0], begin_area[i][0]].min # max = [end_area[i][1], begin_area[i][1]].max # key = axes[i] # plot_size[key + :range] = [min, max] # end # ep plot_size # exit # frames = [] # actual_frames = {} # i = fa[0] # j = 0 # while i <= fa[1] # frames.push i # actual_frames[i] = j # i += (options[:ic] or options[:increment] or 1) # j += 1 # end i = 0 actual_frames = graphkit_frame_array.map{|f, g| f}.inject({}){|hash, f| hash[f] = i; i+=1; hash} graphkit_frame_array.pieces(no_forks).each_with_index do |graphkit_frame_array_piece, myrank| fork do # if options[:normalize_pieces] # end_area = graphkit_multiple_runners(list, frame_index: piece.max).plot_area_size # begin_area = graphkit_multiple_runners(list, frame_index: piece.min).plot_area_size # axes = [:x, :y, :z] # for i in 0...end_area.size # next unless options[:normalize_pieces].include? axes[i] # min = [end_area[i][0], begin_area[i][0]].min # max = [end_area[i][1], begin_area[i][1]].max # key = axes[i] # # plot_size[key + :range] = [min, max] # end # end # eputs 'making graphs...'; sleep 1 # graph_array = graphkit_multiple_runners_with_frame_array(piece, list, myrank==0) # # ep graph_array # eputs graphkit_frame_array_piece.each_with_index do |(frame_index,g), pindex| if myrank == 0 eputs "\033[2A" # Terminal jargon - go back one line eputs sprintf("Plotting graphs: %2.2f", pindex.to_f/graphkit_frame_array_piece.size.to_f * 100.0) + "% Complete" end # g = graph_kit_multiple_runners(list, plot_size + {frame_index: frame_index}) # g.modify(plot_size) # g.modify(options) # p g; exit # g.title += sprintf(", frame %0#{fd}d", frame_index) unless options[:frame_title] == false folder = ("film_frames/"); file_name = sprintf("frame_%0#{fd}d", actual_frames[frame_index]) # g.gnuplot; gets; g.close # ep folder + file_name + '.png'; gets g.gnuplot_write(folder + file_name + extension, size: options[:size]) end end end eputs "Waiting on subprocesses..." Process.waitall end unless options[:skip_encoding] eputs "making film" frame_rate = (options[:frame_rate] or options[:fr] || 15) film_name = (options[:film_name] or options [:fn] or graphkit_frame_array[0][1].file_name + '_film').gsub(/\s/, '_') puts `ffmpeg -y #{options[:bitrate] ? "-b #{options[:bitrate]}" : ""} -r #{frame_rate} -threads #{(@multiple_processes or 1)} -i film_frames/frame_%0#{fd}d#{extension} -qscale 0 #{film_name}.mp4` end end end