[[iii{9"$lib/tracksperanto/block_init.rb{: lines[ "[# Implements the conventional constructor with "hash of attributes" and block support "%module Tracksperanto::BlockInit "2 def initialize(object_attribute_hash = {}) "A object_attribute_hash.map { |(k, v)| send("#{k}=", v) } "% yield(self) if block_given? " end " end : counts[ iiiQFi- iii: coverage[ : inferredTTTT;;"lib/import/syntheyes.rb{;[&"Jclass Tracksperanto::Import::Syntheyes < Tracksperanto::Import::Base ", include Tracksperanto::UVCoordinates " " def self.human_name "+ "Syntheyes 2D tracker paths file" " end " " def parse(io) " trackers = [] "" io.each_line do | line | "8 name, frame, x, y, frame_status = line.split " ". # Do we already have this tracker? "2 t = trackers.find {|e| e.name == name} " if !t "; report_progress("Allocating tracker #{name}") "@ t = Tracksperanto::Tracker.new{|t| t.name = name } " trackers << t " end " " # Add the keyframe "> t.keyframes << Tracksperanto::Keyframe.new do |e| " e.frame = frame "1 e.abs_x = convert_from_uv(width, x) "N e.abs_y = height - convert_from_uv(height, y) # Convert TL to BL " end "B report_progress("Adding keyframe #{frame} to #{name}") " end " " trackers " end " "end;[&iiiiiiii i ii'iiiNiN$i>iwi>iiiii'i'i'i'i'iiiiii;[&TT;TTT;TTTT;;TTTTT;;;TTTTTT;;T;;;")lib/tracksperanto/format_detector.rb{;[+"n# Finds a suitable importer for the chosen file path. Or at least tries to, based on the file extension. "^# Will then examine all the importers and ask them if they can handle the specified file ")class Tracksperanto::FormatDetector " "! def initialize(with_path) "& perform_detection(with_path) " freeze " end " " private ") def perform_detection(for_path) "= return unless (for_path && !for_path.to_s.empty?) "1 ext = File.extname(for_path.downcase) "\ @importer_klass = Tracksperanto.importers.find{ |i| i.distinct_file_ext == ext } " end " " public " "B # Tells if an importer has been found for this extension " def match? " !!@importer_klass " end " "0 # Returns the importer if there is one " def importer_klass " @importer_klass " end " "3 # Tells if comp size needs to be provided " def auto_size? "= match? ? importer_klass.autodetects_size? : false " end " "2 # Returns the human name of the importer "! def human_importer_name "A match? ? importer_klass.human_name : "Unknown format" " end "end;[+iiiii i iiiii iiiiiiiiiiiiii i i iii i iiii i ii;[+;;T;TTT;;TTTTT;;T;;TTT;;TTT;;TTT;;TTT;"&lib/import/shake_grammar/lexer.rb{;[|"(module Tracksperanto::ShakeGrammar ", class WrongInput < RuntimeError; end " "U # Since Shake uses a C-like language for it's scripts we rig up a very sloppy "* # but concise C-like lexer to cope " class Lexer " " # Parsed stack " attr_reader :stack " ") # Access to the sentinel object " attr_reader :sentinel " "' STOP_TOKEN = :__stop #:nodoc: "! MAX_BUFFER_SIZE = 32000 " MAX_STACK_DEPTH = 127 " "P # The first argument is the IO handle to the data of the Shake script. "J # The second argument is a "sentinel" that is going to be passed "M # to the downstream lexers instantiated for nested data structures. "R # You can use the sentinel to collect data from child nodes for example. "] def initialize(with_io, sentinel = nil, limit_to_one_stmt = false, stack_depth = 0) " @io, @stack, @buf, @sentinel, @limit_to_one_stmt, @stack_depth = with_io, [], '', sentinel, limit_to_one_stmt, stack_depth "6 catch(STOP_TOKEN) { parse until @io.eof? } "? in_comment? ? consume_comment("\n") : consume_atom! " end " " private " " def in_comment? "2 (@buf.strip =~ /^\/\//) ? true : false " end " " def consume_comment(c) "" if c == "\n" # Comment "= push [:comment, @buf.gsub(/(\s+?)\/\/{1}/, '')] " @buf = '' " else " @buf << c " end " end " " def parse " " c = @io.read(1) " "] if @buf.length > MAX_BUFFER_SIZE # Wrong format and the buffer is filled up, bail "_ raise WrongInput, "Buffer overflow at 32K, this is definitely not a Shake script" " end " "S if @stack_depth > MAX_STACK_DEPTH # Wrong format - parentheses overload "s raise WrongInput, "Stack overflow at level 128, this is probably a LISP program uploaded by accident" " end " "5 return consume_comment(c) if in_comment? " "3 if !@buf.empty? && (c == "(") # Funcall "j push([:funcall, @buf.strip] + self.class.new(@io, @sentinel, false, @stack_depth + 1).stack) " @buf = '' "+ elsif c == "[" # Array, booring "5 push([:arr, self.class.new(@io).stack]) "J elsif (c == "]" || c == ")" || c == ";" && @limit_to_one_stmt) ". # Bailing out of a subexpression " consume_atom! " throw STOP_TOKEN " elsif (c == ",") " consume_atom! " elsif (c == "@") " consume_atom! " @buf << c ") elsif (c == ";" || c == "\n") "I # Skip these - the subexpression already is expanded anyway " elsif (c == "=") "{ push [:assign, @buf.strip, self.class.new(@io, @sentinel, to_semicolon = true, @stack_depth + 1).stack.shift] " @buf = '' " else " @buf << c " end " end " " INT_ATOM = /^(\d+)$/ "& FLOAT_ATOM = /^([\-\d\.]+)$/ " STR_ATOM = /^\"/ " AT_FRAME = /^@(\d+)/ " ") # Grab the minimum atomic value " def consume_atom! "% at, @buf = @buf.strip, '' " return if at.empty? " " the_atom = case at " when INT_ATOM " at.to_i " when STR_ATOM " unquote_s(at) " when FLOAT_ATOM " at.to_f " when AT_FRAME "# if $1.include?(".") "2 [:value_at, $1.to_f, @stack.pop] " else "2 [:value_at, $1.to_i, @stack.pop] " end " else " [:atom, at] " end " " push(the_atom) " end " " def unquote_s(string) "J string.strip.gsub(/^\"/, '').gsub(/\"$/, '').gsub(/\\\"/, '"') " end " " def push(atom_array) " @stack << atom_array " end " end "end;[|iiiiiiiiiiiiiiiiiiiiii*iR iR iN iiiii[- i!iZ- ii ii7i7ii] ii ii' iijOiijOiiiihOiiiifOiiY iPii i0iL1iiiiD&iilTi~i~ipiiiiiiGT iiiiiiiiiii&ai&aii i i i)iiiiQiixiiiuiiiiii iiiiiiiiiii;[|TT;;;T;;T;;T;TTT;;;;;TTTT;;T;TTT;TTTT;T;T;T;T;TT;;TT;;T;TTTTTT;TTTTTTTT;TTT;T;;;TTTT;;TTT;TTTTTTTTTF;T;;T;;T;;TTT;TTT;;"lib/export/match_mover.rb{;[-"4# Export for Autodesk MatchMover/Image Modeler "Kclass Tracksperanto::Export::MatchMover < Tracksperanto::Export::Base " "# def self.desc_and_extension " "matchmover.rz2" " end " " def self.human_name "* "Matchmover 2D export .rz2 file" " end " "v PREAMBLE = %[imageSequence "Sequence 01"\n{\n\t%d\t%d\tf( "D:/temp/sequence.%%04d.dpx" )\tb( 1 211 1 )\t\n}\n] "A TRACKER_PREAMBLE = "pointTrack %s rgb( 255 0 0 ) \n{\n" "! TRACKER_POSTAMBLE = "}\n" "o FIRST_KEYFRAME_TEMPLATE = "\t%d\t %.3f %.3f ki( 0.8 )\t s( 66 66 64 64 ) p( 24 24 25 25 )" "E KEYFRAME_TEMPLATE = "\t%d\t %.3f %.3f p+( %.6f )\t" " "0 def start_export( img_width, img_height) " @height = img_height "6 @io.puts(PREAMBLE % [img_width, img_height]) " end " "/ def start_tracker_segment(tracker_name) "& @tracker_name = tracker_name " @at_first_point = true "< @io.write(TRACKER_PREAMBLE % tracker_name.inspect) " end " " def end_tracker_segment "& @io.write(TRACKER_POSTAMBLE) " end " "I def export_point(frame, abs_float_x, abs_float_y, float_residual) "R template = @at_first_point ? FIRST_KEYFRAME_TEMPLATE : KEYFRAME_TEMPLATE "X values = [frame + 1, abs_float_x, @height - abs_float_y, (1 - float_residual)] "% @io.puts(template % values) "! @at_first_point = false " end " "end;[-iiiiiiiiiiiiiiiiiii iiiii iiiiiiiii,iwiQi+i+iii;[-;T;TTT;TTT;TTTTT;TTT;;TTTT;;TTT;TTTTT;;;"!lib/tracksperanto/tracker.rb{;[6"# Internal representation of a tracker point with keyframes. A Tracker is an array of Keyframe objects and should act and work like one "9class Tracksperanto::Tracker < DelegateClass(Array) "$ include Tracksperanto::Casts " include Comparable " "* # Contains the name of the tracker " attr_accessor :name " cast_to_string :name " "2 def initialize(object_attribute_hash = {}) " @name = "Tracker" " __setobj__(Array.new) "A object_attribute_hash.map { |(k, v)| send("#{k}=", v) } "% yield(self) if block_given? " end " "$ def keyframes=(new_kf_array) "& __setobj__(new_kf_array.dup) " end " " def keyframes " __getobj__ " end " "= # Trackers sort by the position of the first keyframe " def <=>(other_tracker) "2 self[0].frame <=> other_tracker[0].frame " end " "6 # Automatically truncate spaces in the tracker "0 # name and replace them with underscores " def name=(n) "+ @name = n.to_s.gsub(/(\s+)/, '_') " end " "4 # Create and save a keyframe in this tracker " def keyframe!(options) "4 push(Tracksperanto::Keyframe.new(options)) " end " " def inspect "8 "" " end " "D # Hack - prevents the Tracker to be flattened into keyframes "9 # when an Array of Trackers gets Array#flatten'ed " def to_ary; end " private :to_ary "end;[6iiiiiiiiiiii~iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii;[6;TTT;;TT;TTTTT;;TTT;TTT;;TFF;;;TTT;;TTT;TTT;;;TT;"lib/middleware/slipper.rb{;["# Slips the keyframe positions by a specific integer amount of frames, positive values slip forward (later in time). Useful if you just edited some stuff onto "F# the beginning if your sequence and need to extend your tracks. "Pclass Tracksperanto::Middleware::Slipper < Tracksperanto::Middleware::Base " DEFAULT_SLIP = 0 " attr_accessor :slip " cast_to_int :slip " " def slip "$ @slip.to_i || DEFAULT_SLIP " end " "A def export_point(frame, float_x, float_y, float_residual) "? super(frame + slip, float_x, float_y, float_residual) " end "end;[iiiiiiii iiiiiii;[;;TTTT;TTT;TTTT"lib/import/shake_text.rb{;[#" "Jclass Tracksperanto::Import::ShakeText < Tracksperanto::Import::Base " " def self.human_name "# "Shake .txt tracker file" " end " " def parse(io) " trackers = [] " io.each do | line | "' if line =~ /TrackName (.+)/ "F trackers << Tracksperanto::Tracker.new{|t| t.name = $1 } "7 # Toss the next following string - header " io.gets " else "* keyframe_values = line.split "0 next if keyframe_values.length < 4 " "M trackers[-1].keyframes << Tracksperanto::Keyframe.new do | kf | "8 kf.frame = (keyframe_values[0].to_i - 1) "- kf.abs_x = keyframe_values[1] "- kf.abs_y = keyframe_values[2] "; kf.residual = (1 - keyframe_values[3].to_f) " end " end " end " " trackers " end "end;[#iiiiiiiii iii iiii~iiiii{i{ii{iiiiii;[#;T;TTT;TTTTT;T;TT;TTTTTT;;;T;;"lib/import/pftrack.rb{;[1"Hclass Tracksperanto::Import::PFTrack < Tracksperanto::Import::Base " def self.human_name " "PFTrack .2dt file" " end " "" def self.distinct_file_ext " ".2dt" " end " "( CHARACTERS_OR_QUOTES = /[AZaz"]/ " " def parse(io) " trackers = [] " until io.eof? " line = io.gets "+ next if (!line || line =~ /^#/) " "A if line =~ CHARACTERS_OR_QUOTES # Tracker with a name "T t = Tracksperanto::Tracker.new{|t| t.name = line.strip.gsub(/"/, '') } ": report_progress("Reading tracker #{t.name}") "" parse_tracker(t, io) " trackers << t " end " end " " trackers " end " " private "" def parse_tracker(t, io) ". first_tracker_line = io.gets.chomp " "T if first_tracker_line =~ CHARACTERS_OR_QUOTES # PFTrack version 5 format "0 first_tracker_line = io.gets.chomp " end " "6 num_of_keyframes = first_tracker_line.to_i "G t.keyframes = (1..num_of_keyframes).map do | keyframe_idx | "e report_progress("Reading keyframe #{keyframe_idx} of #{num_of_keyframes} in #{t.name}") "5 f, x, y, residual = io.gets.chomp.split "p Tracksperanto::Keyframe.new(:frame => f, :abs_x => x, :abs_y => y, :residual => residual.to_f * 8) " end " end "end;[1iiiiiiiiiiii i iiiiiiRi)i)i)iiiiiiii*iRiiRiiii)iRiiiiii;[1TTTT;TTT;T;TTTTT;TTTTT;;;T;;TTT;TT;;TTTTT;;;"lib/import/base.rb{;[L"# The base class for all the import modules. By default, when you inherit from this class the inherited class will be included "# in the list of supported Tracksperanto importers. The API that an importer should present is very basic, and consists only of a few methods. "U# The main method is parse(io) which should return an array of Tracker objects. "'class Tracksperanto::Import::Base "% include Tracksperanto::Safety "$ include Tracksperanto::Casts "( include Tracksperanto::BlockInit "( include Tracksperanto::ZipTuples "( include Tracksperanto::ConstName " "_ # Tracksperanto will assign a proc that reports the status of the import to the caller. "] # This block is automatically used by report_progress IF the proc is assigned. Should "h # the proc be nil, the report_progress method will just pass (so you don't need to check for nil " # yourself) "% attr_accessor :progress_block " "2 # The original width of the tracked image. "Z # If you need to know the width for your specific format and cannot autodetect it, "f # Trakcksperanto will assign the passed width and height to the importer object before running "i # the import. If not, you can replace the assigned values with your own. At the end of the import "c # procedure, Tracksperanto will read the values from you again and will use the read values "g # for determining the original comp size. +width+ and +height+ MUST return integer values after " # the import completes " attr_accessor :width " "N # The original height of the comp, same conventions as for width apply " attr_accessor :height " "A # These reader methods will raise when the values are nil "# cast_to_int :width, :height "# safe_reader :width, :height " "J # Used to register your importer in the list of supported formats. "6 # Normally you would not need to override this " def self.inherited(by) "' Tracksperanto.importers << by " super " end " "R # Return an extension WITH DOT if this format has a typical extension that "/ # you can detect (like ".nk" for Nuke) "" def self.distinct_file_ext " nil " end " "a # Should return a human-readable (read: properly capitalized and with spaces) name of the " # import format " def self.human_name "" "Abstract import format" " end " "e # Return true from this method if your importer can deduce the comp size from the passed file "" def self.autodetects_size? " false " end " "X # Call this method from the inside of your importer to tell what you are doing. "~ # This gets propagated to the caller automatically, or gets ignored if the caller did not request any progress reports "$ def report_progress(message) ": @progress_block.call(message) if @progress_block " end " "i # The main method of the parser. Will receive an IO handle to the file being imported, and should "g # return an array of Tracksperanto::Tracker objects containing keyframes. If you have a problem "l # doing an import, raise from here. Note that in general it's a good idea to stream-parse a document "l # instead of bulk-reading it into memory (since Tracksperanto tries to be mem-efficient when dealing " # with large files) " def parse(track_file_io) " [] " end "end;[Liiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii i i iiii,iYi,iiiiiiiiii;m;[L;;;TTTTTT;;;;;T;;;;;;;;T;;T;;TT;;;TTT;;;;TTT;;;TFF;;TTT;;;TTT;;;;;;TFFT"lib/export/mux.rb{;[-"?# Multiplexor. Accepts a number of exporters and replays "/# the calls to all of them in succession. "&class Tracksperanto::Export::Mux " attr_accessor :outputs " " def initialize(*outputs) "$ @outputs = outputs.flatten " end " " # Called on export start "0 def start_export( img_width, img_height) "% @outputs.each do | output | "7 output.start_export( img_width, img_height) " end " end " "7 # Called on tracker start, one for each tracker "/ def start_tracker_segment(tracker_name) "% @outputs.each do | output | "6 output.start_tracker_segment(tracker_name) " end " end " " def end_tracker_segment "% @outputs.each do | output | "& output.end_tracker_segment " end " end " "* # Called for each tracker keyframe "N def export_point(at_frame_i, abs_float_x, abs_float_y, float_residual) "% @outputs.each do | output | "U output.export_point(at_frame_i, abs_float_x, abs_float_y, float_residual) " end " end " " def end_export "* @outputs.each{|o| o.end_export } " end "end;[-iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii ii;[-;;TT;TTT;;TTT;T;;TTT;T;TTT;T;;TTT;T;TTT;"lib/middleware/scaler.rb{;['"`# Scales the comp being exported by a specific factor, together with the tracker keyframes "Oclass Tracksperanto::Middleware::Scaler < Tracksperanto::Middleware::Base " DEFAULT_FACTOR = 1 " "* attr_accessor :x_factor, :y_factor "* cast_to_float :x_factor, :y_factor " " # Called on export start "0 def start_export( img_width, img_height) " set_residual_factor "K super( (img_width * x_factor).to_i, (img_height * y_factor).to_i) " end " " def y_factor "% @y_factor || DEFAULT_FACTOR " end " " def x_factor "% @x_factor || DEFAULT_FACTOR " end " "A def export_point(frame, float_x, float_y, float_residual) " super(frame, "" (float_x * x_factor), "" (float_y * y_factor), "/ (float_residual * @residual_factor) " ) " end " " private "! def set_residual_factor "K @residual_factor = Math.sqrt((x_factor ** 2) + (y_factor ** 2)) " end "end;['iiiiiiiii i iiiiiiiiiiiiiiiiiiiii iii ;[';TT;TT;;TTT;;TTT;TTT;T;TTTTT;TTTTT"lib/middleware/prefix.rb{;["[# This middleware prepends the names of the trackers passing through it with a prefix "# and an underscore "Oclass Tracksperanto::Middleware::Prefix < Tracksperanto::Middleware::Base " attr_accessor :prefix " cast_to_string :prefix " "/ def start_tracker_segment(tracker_name) "? prefixed_name = [prefix.gsub(/_$/, ''), tracker_name] ". prefixed_name.reject!{|e| e.empty? } "( super(prefixed_name.join('_')) " end " "end;[iiiiiiii i iiii ;[;;TTT;TTTT;;T"lib/import/nuke_script.rb{;[e"require 'delegate' " "Kclass Tracksperanto::Import::NukeScript < Tracksperanto::Import::Base " " def self.human_name " "Nuke .nk script file" " end " "" def self.distinct_file_ext " ".nk" " end " " def parse(io) "? scan_for_tracker3_nodes(Tracksperanto::ExtIO.new(io)) " end " " private " ", TRACKER_3_PATTERN = /^Tracker3 \{/ ") TRACK_PATTERN = /^track(\d) \{/ "% NODENAME = /^name ([^\n]+)/ " ") def scan_for_tracker3_nodes(io) " tracks = [] "* while line = io.gets_and_strip "J tracks += scan_tracker_node(io) if line =~ TRACKER_3_PATTERN " end " tracks " end " "# def scan_tracker_node(io) "" tracks_in_tracker = [] "* while line = io.gets_and_strip "& if line =~ TRACK_PATTERN ") t = extract_tracker(line) ". tracks_in_tracker.push(t) if t "$ elsif line =~ NODENAME "= tracks_in_tracker.each_with_index do | t, i | ". t.name = "#{$1}_track#{i+1}" "G report_progress("Scavenging Tracker3 node #{t.name}") " end "( return tracks_in_tracker " end " end "B raise "Tracker node went all the way to end of stream" " end " ") def scan_track(line_with_curve) "e x_curve, y_curve = line_with_curve.split(/\}/).map{ | one_curve| parse_curve(one_curve) } "2 return nil unless (x_curve && y_curve) ". zip_curve_tuples(x_curve, y_curve) " end " "$ SECTION_START = /^x(\d+)$/ "# KEYFRAME = /^([-\d\.]+)$/ " "@ # Scan a curve to a number of tuples of [frame, value] "% def parse_curve(curve_text) "k # Replace the closing curly brace with a curly brace with space so that it gets caught by split "B atoms, tuples = curve_text.gsub(/\}/m, ' }').split, [] " # Nuke saves curves very efficiently. x(keyframe_number) means that an uninterrupted sequence of values will start, "{ # after which values follow. When the curve is interrupted in some way a new x(keyframe_number) will signifu that we "{ # skip to that specified keyframe and the curve continues from there, in gap size defined by the last fragment. "P # That is, x1 1 x3 2 3 4 will place 2, 3 and 4 at 2-frame increments " "' last_processed_keyframe = 1 "# intraframe_gap_size = 1 "$ while atom = atoms.shift "& if atom =~ SECTION_START "1 last_processed_keyframe = $1.to_i " if tuples.any? "5 last_captured_frame = tuples[-1][0] "U intraframe_gap_size = last_processed_keyframe - last_captured_frame " end "% elsif atom =~ KEYFRAME "T report_progress("Reading curve at frame #{last_processed_keyframe}") "< tuples << [last_processed_keyframe, $1.to_f] "> last_processed_keyframe += intraframe_gap_size " elsif atom == '}' " return tuples " end " end " tuples " end " "# def extract_tracker(line) "% tuples = scan_track(line) "5 return nil unless (tuples && tuples.any?) " "' Tracksperanto::Tracker.new( "8 :keyframes => tuples.map do | (f, x, y) | "V Tracksperanto::Keyframe.new(:frame => f -1, :abs_x => x, :abs_y => y) " end " ) " end "end;[eiiiiiiiiiiiii i i iiiiiiii ii iii iiii!iii9imiii i iiiiiiii:iii iiiiiiiViiiiiiiiUiUiUiiLiiiii i i i iiiiiTiii:imiii iiiiii;[eT;T;TTT;TTT;TTT;T;TTT;TTTT;T;;TTTTTTTTTT;T;;FF;TTTT;;TT;;T;T;;;;;TTTTTTTT;TTTTTT;;T;;TTT;T;T;;;;"'lib/tracksperanto/flame_builder.rb{;[R"7# A Builder-like class for exporting Flame setups "'class Tracksperanto::FlameBuilder " INDENT = "\t" " "& def initialize(io, indent = 0) "# @io, @indent = io, indent " end " "1 def write_block!(name, value = nil, &blk) "E value.nil? ? write_loose!(name) : write_tuple!(name, value) "1 yield(self.class.new(@io, @indent + 1)) "2 @io.puts(INDENT * (@indent + 1) + "End") " end " "> def write_unterminated_block!(name, value = nil, &blk) "E value.nil? ? write_loose!(name) : write_tuple!(name, value) "1 yield(self.class.new(@io, @indent + 1)) " end " "$ def write_tuple!(key, value) "V @io.puts("%s%s %s" % [INDENT * @indent, __camelize(key), __flameize(value)]) " end " " def write_loose!(value) "B @io.puts("%s%s" % [INDENT * @indent, __camelize(value)]) " end " "$ def linebreak!(how_many = 1) "$ @io.write("\n" * how_many) " end " "/ def color_hash!(name, red, green, blue) "2 write_unterminated_block!(name) do | b | " b.red(red) " b.green(green) " b.blue(blue) " end " end " "$ def <<(some_verbatim_string) "; some_verbatim_string.split("\n").each do | line | "1 @io.puts(["\t" * @indent, line].join) " end " end " " private " "+ def method_missing(meth, arg = nil) " if block_given? "3 write_block!(meth, arg) {|c| yield(c) } " else " if arg.nil? " write_loose!(meth) " else "% write_tuple!(meth, arg) " end " end " end " " def __camelize(s) " @@camelizations ||= {} "F @@camelizations[s] ||= s.to_s.gsub(/(^|_)(.)/) { $2.upcase } " end " " def __flameize(v) " case v " when Float " "%.3f" % v " when TrueClass " "yes" " when FalseClass " "no" " else " v.to_s " end " end "end;[Riiiiiiiiiii}iiiiii iiiiiiiiiiiiiii i i i i ii iiiiiiiiiiiiiii iiDiiiiiiiiiiiiiiiiiiiiii;[R;TT;TTT;TTTT;;TTT;;TTT;TTT;TTT;TTTTT;T;TFFFF;T;TTT;TT;T;;T;TTT;;TTTTTTTT;T;T;"lib/tracksperanto.rb{;[q"require 'stringio' "require 'delegate' "require 'tempfile' " "module Tracksperanto "7 PATH = File.expand_path(File.dirname(__FILE__)) " VERSION = '1.9.0' " " module Import; end " module Export; end " module Middleware; end " module Pipeline; end " " class << self "= # Returns the array of all exporter classes defined "" attr_accessor :exporters " "= # Returns the array of all importer classes defined "" attr_accessor :importers " ": # Returns the array of all available middlewares "$ attr_accessor :middlewares " "2 # Returns the names of all the importers " def importer_names ", importers.map{|e| e.const_name } " end " "2 # Returns the names of all the exporters " def exporter_names ", exporters.map{|e| e.const_name } " end " "4 # Returns the names of all the middlewares " def middleware_names ". middlewares.map{|e| e.const_name } " end " end " "E self.exporters, self.importers, self.middlewares = [], [], [] " "@ # Case-insensitive search for a middleware class by name "% def self.get_middleware(name) "# middlewares.each do | x | "> return x if x.const_name.downcase == name.downcase " end " "7 raise NameError, "Unknown middleware #{name}" " end " "> # Case-insensitive search for an export module by name "# def self.get_exporter(name) "! exporters.each do | x | "> return x if x.const_name.downcase == name.downcase " end " "5 raise NameError, "Unknown exporter #{name}" " end " "> # Case-insensitive search for an export module by name "# def self.get_importer(name) "! importers.each do | x | "> return x if x.const_name.downcase == name.downcase " end " "5 raise NameError, "Unknown importer #{name}" " end " end " " %w( " returning " const_name " casts " block_init " safety " zip_tuples " keyframe " tracker " format_detector " ext_io " progressive_io " buffer_io " simple_export " uv_coordinates " flame_builder ").each do | submodule | "J require File.join(Tracksperanto::PATH, "tracksperanto", submodule) " end " "# Load importers "JDir.glob(File.dirname(__FILE__) + '/import/*.rb').sort.each do | i | " require i " end " "# Load exporters "JDir.glob(File.dirname(__FILE__) + '/export/*.rb').sort.each do | i | " require i " end " "# Load middleware "NDir.glob(File.dirname(__FILE__) + '/middleware/*.rb').sort.each do | i | " require i " end " "# Load pipelines "LDir.glob(File.dirname(__FILE__) + '/pipeline/*.rb').sort.each do | i | " require i "end;[qiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii iiiiiii i iUiiiiiiii i-iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii;[qTTT;TTT;TTTT;T;T;;T;;T;;TTT;;TTT;;TTT;;T;;TTT;FFF;;TTT;;T;;;TTT;FFFF;;;T;;;;;;;;;;;;;;T;;;TT;;;TT;;;TT;;;TT;"$lib/middleware/length_cutoff.rb{;["T# This middleware removes trackers that contain less than min_length keyframes "# from the exported batch "Uclass Tracksperanto::Middleware::LengthCutoff < Tracksperanto::Middleware::Base "! attr_accessor :min_length " cast_to_int :min_length " "' def start_tracker_segment(name) "> @tracker = Tracksperanto::Tracker.new(:name => name) " end " " def end_tracker_segment "I return if ((min_length > 0) && (@tracker.length < min_length)) " "8 @exporter.start_tracker_segment(@tracker.name) "` @tracker.each{|kf| @exporter.export_point(kf.frame, kf.abs_x, kf.abs_y, kf.residual) } "' @exporter.end_tracker_segment " end " "A def export_point(frame, float_x, float_y, float_residual) "o @tracker.keyframe! :abs_x => float_x, :abs_y => float_y, :residual => float_residual, :frame => frame " end " " "end;[iiiiiiiiiiiiiiiiiiiiiiii ;[;;TTT;TTT;TT;TTT;;TTT;;T"#lib/import/flame_stabilizer.rb{;[" "Pclass Tracksperanto::Import::FlameStabilizer < Tracksperanto::Import::Base " "5 # Flame setups contain clear size indications "" def self.autodetects_size? " true " end " "" def self.distinct_file_ext " ".stabilizer" " end " " def self.human_name "" "Flame .stabilizer file" " end " "" class ChannelBlock < Array "( include ::Tracksperanto::Casts " cast_to_string :name "# cast_to_float :base_value " " def <=>(o) " @name <=> o.name " end " "* def initialize(io, channel_name) "& @name = channel_name.strip " "4 base_value_matcher = /Value ([\-\d\.]+)/ "1 keyframe_count_matcher = /Size (\d+)/ " indent = nil " " keyframes = [] " " while line = io.gets " " unless indent "/ indent = line.scan(/^(\s+)/)[1] ") end_mark = "#{indent}End" " end " "/ if line =~ keyframe_count_matcher "< $1.to_i.times { push(extract_key_from(io)) } "8 elsif line =~ base_value_matcher && empty? "$ self.base_value = $1 "* elsif line.strip == end_mark " break " end " end " "Z raise "Parsed a channel #{@name} with no keyframes" if (empty? && !base_value) " end " "" def extract_key_from(io) " frame = nil "/ frame_matcher = /Frame ([\-\d\.]+)/ "/ value_matcher = /Value ([\-\d\.]+)/ " " until io.eof? " line = io.gets "& if line =~ frame_matcher " frame = $1.to_i ") elsif line =~ value_matcher "' return [frame, $1.to_f] " end " end " "1 raise "Did not detect any keyframes!" " end " "F # Hack - prevents the channel to be flattened into keyframes ") # when it gets Array#flatten'ed " def to_ary; end " private :to_ary " end " " def parse(io) "2 report_progress("Extracting setup size") "L self.width, self.height = extract_width_and_height_from_stream(io) "> report_progress("Extracting all animation channels") "5 channels = extract_channels_from_stream(io) " "Z raise "The setup contained no channels that we could process" if channels.empty? "A raise "A channel was nil" if channels.find{|e| e.nil? } " "D report_progress("Assembling tracker curves from channels") "3 scavenge_trackers_from_channels(channels) " end " " private "6 def extract_width_and_height_from_stream(io) " w, h = nil, nil " "* w_matcher = /FrameWidth (\d+)/ "+ h_matcher = /FrameHeight (\d+)/ " " until io.eof? " line = io.gets "" if line =~ w_matcher " w = $1 "% elsif line =~ h_matcher " h = $1 " end " "' return [w, h] if (w && h) " end " " end " =begin "+Here's how a Flame channel looks like " track_x.name.split('/').shift) " "; report_progress("Extracting tracker #{t.name}") " "P track_y = channels.find{|e| e.name == "#{t.name}/#{REF_CHANNEL}/y" } "G shift_x = channels.find{|e| e.name == "#{t.name}/shift/x" } "G shift_y = channels.find{|e| e.name == "#{t.name}/shift/y" } " "= shift_tuples = zip_curve_tuples(shift_x, shift_y) "= track_tuples = zip_curve_tuples(track_x, track_y) " "" base_x, base_y = begin "5 report_progress("Detecting base value") "; find_base_x_and_y(track_tuples, shift_tuples) " rescue UseBase "6 [track_x.base_value, track_y.base_value] " end " " total_kf = 1 "< t.keyframes = shift_tuples.map do | (at, x, y) | "S # Flame keyframes are sort of minus-one based, so to start at frame 0 "Z # we need to decrement one frame, always. Also, the shift value is inverted! "; kf_x, kf_y = base_x - x.to_f, base_y - y.to_f " "R report_progress("Extracting keyframe #{total_kf += 1} of #{t.name}") "] Tracksperanto::Keyframe.new(:frame => (at - 1), :abs_x => kf_x, :abs_y => kf_y) " end " " return t " end " " UseBase = RuntimeError " "; def find_base_x_and_y(track_tuples, shift_tuples) "C base_track_tuple = track_tuples.find do | track_tuple | "S shift_tuples.find { |shift_tuple| shift_tuple[0] == track_tuple [0] } " end " if base_track_tuple "$ base_track_tuple[1..2] "! elsif track_tuples[0] "# track_tuples[0][1..2] " else " raise UseBase " end " end "end;[iiiiiiiii iiiiiiiiiiiiiiiiii`iiiiiiiiiiii ii iiiii ii iiiiiiii;itFi:i:ii:ikiri:ii:iiiiiiiiiiiii i iiiii iiiiiiii i iiiiiii5iiiiiiMiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii i iiidiiiiiiiiiiiiiii i ii6i6iiiiii7igii6iii^!i!ii6i6ii6i6i6iiiii6i6iiiNiiNiiii6iiiii7igiiigi6iiiiiiih;[;T;;TTT;TTT;TTT;TTTT;TFF;TT;TTT;T;T;TTT;;TTTTTT;;;T;;TTTT;TTTTTT;;FFF;;;TT;;TTTTT;TT;TT;;TTT;TT;TTTTTT;;T;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;TTTTTTTT;;T;;TTT;T;TTTTT;;T;;TT;T;TTT;TT;TTTFFF;TT;;T;TT;;T;;T;TTT;TTFFFFFFT"lib/import/boujou.rb{;[>"Gclass Tracksperanto::Import::Boujou < Tracksperanto::Import::Base " " def self.human_name "( "Boujou feature tracks export" " end " " def parse(io) "3 wrapped_io = Tracksperanto::ExtIO.new(io) "$ detect_columns(wrapped_io) " trackers = {} "F filtering_trackers_from(wrapped_io) do | name, frame, x, y | "H trackers[name] ||= Tracksperanto::Tracker.new(:name => name) "C report_progress("Extracting frame #{frame} of #{name}") "p trackers[name].keyframe!(:frame => (frame.to_i - 1), :abs_y => (@height.to_f - y.to_f), :abs_x => x) " end "8 trackers.values.sort{|a,b| a.name <=> b.name } " end " " private " " COMMENT = /^# / " " def detect_columns(io) " until io.eof? do "$ line = io.gets_and_strip "$ if line =~ /^# track_id/ "2 report_progress("Detecting columns") "+ return set_columns_from(line) " end " end " end " "" def set_columns_from(line) "1 @columns = line.gsub(COMMENT, '').split " end " " # " # "( # # track_id view x y "- # Target_track_1 5 252.046 171.677 "G def filtering_trackers_from(io) #:yields: track_id, frame, x, y " until io.eof? "$ line = io.gets_and_strip "" next if comment?(line) "+ column = make_column_hash(line) "O yield(column["track_id"], column["view"], column["x"], column["y"]) " end " end " "" def make_column_hash(line) "1 Hash[*@columns.zip(line.split).flatten] " end " " def comment?(line) " line =~ COMMENT " end "end;[>iiiiiiiiiiii@ i@ i!iiFiiiiiiiii iiiiiiiiiiiiiiiiii@ ii@ i@ iiiiA ii@ iiA i@ i@ i;[>T;TTT;TTTTTTTT;T;;T;T;TTTTTT;;T;TTT;;;;;TTTTTT;T;TTT;TTT;"lib/export/maya_live.rb{;[A"Iclass Tracksperanto::Export::MayaLive < Tracksperanto::Export::Base " "S # Maya Live exports and imports tracks in "aspect units", so a point at 0,0 "T # will be at -1.78,-1 in MayaLive coordinates with aspect of 1.78. Therefore "< # we offer an override for the aspect being exported " attr_accessor :aspect " "# def self.desc_and_extension " "mayalive.txt" " end " " def self.human_name "! "MayaLive track export" " end " "0 def start_export( img_width, img_height) "1 @aspect ||= img_width.to_f / img_height " "( @w, @h = img_width, img_height " "L first_line = (%w( # Size) + [@w, @h, "%.2f" % @aspect]).join(" ") " @io.puts(first_line) " @tracker_number = 0 " end " "/ def start_tracker_segment(tracker_name) "/ @io.puts('# Name %s' % tracker_name) " end " " def end_tracker_segment " @tracker_number += 1 " end " ", FORMAT_LINE = "%d %d %.10f %.10f %s" " "I def export_point(frame, abs_float_x, abs_float_y, float_residual) " values = [ "# @tracker_number, frame, "? aspectize_x(abs_float_x), aspectize_y(abs_float_y), "/ residual_with_reset(float_residual) " ] "( @io.puts(FORMAT_LINE % values) " end " " private " " def aspectize_x(pix) "6 aspectized_pixel = @w.to_f / (@aspect * 2) ". (pix / aspectized_pixel) - @aspect " end " " def aspectize_y(pix) "* aspectized_pixel = @h.to_f / 2 "( (pix / aspectized_pixel) - 1 " end " "$ def residual_with_reset(r) " "%.10f" % (r/10) " end "end;[Aiiiiiiiiiiiiiiiii iiii iiiii i i ii i i iiiiRiiiiiiQiiiiiRiiiiiRiiiiiRiiQi;[AT;;;;T;TFF;TFF;TT;T;TTT;;TTT;TTT;T;TT;TT;T;;T;TTT;;TTT;;TTT;"lib/export/equalizer4.rb{;[1"%# Export for 3DE v4 point files "Kclass Tracksperanto::Export::Equalizer4 < Tracksperanto::Export::Base " "# def self.desc_and_extension " "3de_v4.txt" " end " " def self.human_name ") "3DE v4 point export .txt file" " end " "0 def start_export( img_width, img_height) "> # 3DE needs to know the number of points in advance, "0 # so we will just buffer to a StringIO "I @internal_io, @num_of_trackers = Tracksperanto::BufferIO.new, 0 " end " "/ def start_tracker_segment(tracker_name) ") @internal_io.puts(tracker_name) " @num_of_trackers += 1 "G @tracker_buffer, @num_of_kfs = Tracksperanto::BufferIO.new, 0 " end " "I def export_point(frame, abs_float_x, abs_float_y, float_residual) "X @tracker_buffer.puts("%d %.15f %.15f" % [frame + 1, abs_float_x, abs_float_y]) " @num_of_kfs += 1 " end " " def end_tracker_segment " @tracker_buffer.rewind "? @internal_io.puts("0") # Color of the point, 0 is red "( @internal_io.puts(@num_of_kfs) "1 @internal_io.puts(@tracker_buffer.read) " @tracker_buffer.close! " end " " def end_export " @internal_io.rewind "$ @io.puts(@num_of_trackers) "% @io.puts(@internal_io.read) " @internal_io.close! " end " " end ;[1iiiiiiiiiiiiiiiiiii iiiii,iQi+iiii iiiiiiiiiiiiii;[1;T;TTT;TTT;T;;TT;TTTT;;TTT;;TTTTTT;;TTTTT;;;"(lib/tracksperanto/uv_coordinates.rb{;["L# Y down X right, 0 is center and values are UV float -1 to 1, doubled ")module Tracksperanto::UVCoordinates " "E # UV coords used by Syntheyes and it's lens distortion algos. ". def absolute_to_uv(abs_x, abs_y, w, h) "A [convert_to_uv(abs_x, w), convert_to_uv(abs_y, h) * -1] " end " "8 def convert_to_uv(absolute_value, absolute_side) "9 x = (absolute_value / absolute_side.to_f) - 0.5 ": # .2 to -.3, y is reversed and coords are double " x * 2 " end " "4 def convert_from_uv(absolute_side, uv_value) "3 # First, start from zero (-.1 becomes .4) "6 value_off_corner = (uv_value.to_f / 2) + 0.5 "* absolute_side * value_off_corner " end "end;[iiiii,i+i+iiRiiiQiiiO$iiliN$ii;[;T;;TTT;TT;T;;T;TT;;"lib/middleware/reformat.rb{;[ "0require File.dirname(__FILE__) + '/scaler' " "j# This middleware reformats (scales) the track setup to a specific pixel resolution. Very useful for "0# applying proxy tracks to full-res images "Qclass Tracksperanto::Middleware::Reformat < Tracksperanto::Middleware::Base " "* # To which format we have to scale "% attr_accessor :width, :height "# cast_to_int :width, :height " " # Called on export start "0 def start_export( img_width, img_height) "/ @width ||= img_width # If they be nil " @height ||= img_height " "W x_factor, y_factor = (@width / img_width.to_f), (@height / img_height.to_f) " " @stash = @exporter "t @exporter = Tracksperanto::Middleware::Scaler.new(@exporter, :x_factor => x_factor, :y_factor => y_factor) " super " end " " def end_export " @exporter = @stash " @exporter.end_export " end "end;[ iiiiiiiiiiiiiiiiiiiiiiiiiii;[ T;;;T;;TT;;TTT;T;TTT;;TTT;;"(lib/import/shake_grammar/catcher.rb{;[F"(module Tracksperanto::ShakeGrammar "_ # Will replay funcalls through to methods if such methods exist in the public insntance " class Catcher < Lexer " class At "% attr_accessor :at, :value " include Comparable " def initialize(a, v) " @at, @value = a, v " end " " def <=>(o) "/ [@at, @value] <=> [o.at, o.value] " end " " def inspect "2 "(#{@value.inspect}@#{@at.inspect})" " end " end " " def push(atom) ", # Send primitive types to parent ". return super if !atom.is_a?(Array) "/ return super if atom[0] != :funcall " "E meth_for_shake_func, args = atom[1].downcase, atom[2..-1] "4 if can_handle_meth?(meth_for_shake_func) "G super([:retval, exec_funcall(meth_for_shake_func, args)]) " else "] # This is a funcall we cannot perform, replace the return result of the funcall "` # with a token to signify that some unknown function's result would have been here "$ super([:unknown_func]) " end " end " " private " "! def can_handle_meth?(m) "6 # Ruby 1.9 - match on stringified methname "S @meths ||= self.class.public_instance_methods(false).map{|mn| mn.to_s } "# @meths.include?(m.to_s) " end " "* def exec_funcall(methname, args) "6 ruby_args = args.map {|a| unwrap_atom(a) } "& send(methname, *ruby_args) " end " " def unwrap_atom(atom) "0 return atom unless atom.is_a?(Array) " " kind = atom.shift " case kind " when :arr "/ atom[0].map{|e| unwrap_atom(e)} " when :retval " atom.shift " when :value_at "; At.new(atom.shift, unwrap_atom(atom.shift)) " else " :unknown " end " end " " end "end;[Fiiiiiii mi mi miiiiiiiiiiisiiVidii[iixiiiiiiiiii\iiq i[iiiyizixiiiiiioioioiini5ijmimiigiiiii;[FT;TTTTTTT;TTT;TFFF;T;TT;TTT;;;T;;;T;T;TT;;TTT;;TT;TTTTTTTT;T;;;;;"lib/import/match_mover.rb{;[I"Kclass Tracksperanto::Import::MatchMover < Tracksperanto::Import::Base " "" def self.autodetects_size? " true " end " " def self.human_name " "MatchMover .rz2 file" " end " "" def self.distinct_file_ext " ".rz2" " end " " def parse(io) " trackers = [] " detect_format(io) " extract_trackers(io) " end " " private " " def detect_format(io) "7 report_progress("Detecting width and height") "' lines = (0..2).map{ io.gets } " last_line = lines[-1] "3 w, h, _ = last_line.scan(/(\d+)/).flatten "* @width, @height = w.to_i, h.to_i " end " " def extract_trackers(io) " tracks = [] "" while(line = io.gets) do "F tracks << extract_track(line, io) if line =~ /^pointTrack/ " end " tracks " end " ") def extract_track(start_line, io) "C tracker_name = start_line.scan(/\"([^\"]+)\"/).flatten[0] "? report_progress("Extracting tracker #{tracker_name}") "? t = Tracksperanto::Tracker.new(:name => tracker_name) "" while(line = io.gets) do "$ return t if line =~ /\}/ "N t.keyframes.push(extract_key(line.strip)) if line =~ /^(\s+?)(\d)/ "2 report_progress("Extracting keyframe") " end "$ raise "Track didn't close" " end " "G LINE_PATTERN = /(\d+)(\s+)([\-\d\.]+)(\s+)([\-\d\.]+)(\s+)(.+)/ " " def extract_key(line) "c frame, x, y, residual, rest = line.scan(LINE_PATTERN).flatten.reject{|e| e.strip.empty? } "& Tracksperanto::Keyframe.new( ") :frame => (frame.to_i() - 1), " :abs_x => x, "8 :abs_y => @height - y.to_f, # Top-left in MM "3 :residual => extract_residual(residual) " ) " end " ". def extract_residual(residual_segment) "M # Parse to the first opening brace and pick the residual from there "# float_pat = /([\-\d\.]+)/ "A 1 - residual_segment.scan(float_pat).flatten.shift.to_f " end "end;[Iiiiiiiiiiiiiiiiiiiiiiiiii iiiiiiii iiiiii ii i iiiiiiiiiiiiiiiiiiiiiiiiii;[IT;TTT;TTT;TTT;TTTT;;T;TTTTTT;;TTTT;T;;TTTTTTTT;FF;T;TTTT;;T;;;T;TT;;"lib/export/equalizer3.rb{;[+"%# Export for 3DE v3 point files "Kclass Tracksperanto::Export::Equalizer3 < Tracksperanto::Export::Base " "M HEADER = '// 3DE Multiple Tracking Curves Export %d x %d * %d frames' " "# def self.desc_and_extension " "3de_v3.txt" " end " " def self.human_name ") "3DE v3 point export .txt file" " end " "0 def start_export( img_width, img_height) "( @w, @h = img_width, img_height "@ # 3DE needs to know the number of keyframes in advance "/ @buffer = Tracksperanto::BufferIO.new " @highest_keyframe = 0 " end " "/ def start_tracker_segment(tracker_name) "$ @buffer.puts(tracker_name) " end " "I def export_point(frame, abs_float_x, abs_float_y, float_residual) " off_by_one = frame + 1 "S @buffer.puts("\t%d\t%.3f\t%.3f" % [off_by_one, abs_float_x, abs_float_y]) "L @highest_keyframe = off_by_one if (@highest_keyframe < off_by_one) " end " " def end_export " @buffer.rewind "8 @io.puts(HEADER % [@w, @h, @highest_keyframe]) "3 @io.puts(@buffer.read) until @buffer.eof? " @buffer.close! "' @io.puts("") # Newline at end " end " end ;[+iiiiiiiiiiiiiii iiiiiiiiii,iQi+iwiiii iiiiii;[+;T;T;TTT;TTT;TT;TT;;TTT;TTTT;;TTTTTT;;"lib/tracksperanto/casts.rb{;[$"T# Helps to define things that will forcibly become floats, integers or strings "!module Tracksperanto::Casts " def self.included(into) " into.extend(self) " super " end " "K # Same as attr_accessor but will always convert to Float internally "& def cast_to_float(*attributes) "( attributes.each do | an_attr | "P define_method(an_attr) { instance_variable_get("@#{an_attr}").to_f } "_ define_method("#{an_attr}=") { |to| instance_variable_set("@#{an_attr}", to.to_f) } " end " end " "T # Same as attr_accessor but will always convert to Integer/Bignum internally "$ def cast_to_int(*attributes) "( attributes.each do | an_attr | "P define_method(an_attr) { instance_variable_get("@#{an_attr}").to_i } "_ define_method("#{an_attr}=") { |to| instance_variable_set("@#{an_attr}", to.to_i) } " end " end " "L # Same as attr_accessor but will always convert to String internally "' def cast_to_string(*attributes) "( attributes.each do | an_attr | "P define_method(an_attr) { instance_variable_get("@#{an_attr}").to_s } "_ define_method("#{an_attr}=") { |to| instance_variable_set("@#{an_attr}", to.to_s) } " end " end "end;[$iii ii iiii i i iԨ ii iii i ipiii iii ii%i iii;[$;TTTT;;;TTTT;T;;TTTT;T;;TTTT;T;"lib/middleware/golden.rb{;["A# This middleware marks all trackers as being 100% accurate "Oclass Tracksperanto::Middleware::Golden < Tracksperanto::Middleware::Base " attr_accessor :enabled " " def enabled " @enabled || false " end " "A def export_point(frame, float_x, float_y, float_residual) "J super(frame, float_x, float_y, (enabled ? 0.0 : float_residual)) " end " "end;[iiiii iiiiiiii;[;TT;TTT;TTT;;"lib/export/syntheyes.rb{;[8"v# Export for Syntheyes tracker UVs. We actually just use prebaked sample values, and do not use bitmasks such as "# OUTCOME_RUN = 1 "8# OUTCOME_ENABLE = 2 -- mirrors the enable track "=# OUTCOME_OK = 4 -- usable u/v present on this frame "E# OUTCOME_KEY = 8 -- there is a key here (OK will be on too) "# OUTCOME_JUMPED = 16 "# OUTCOME_OUTASIGHT = 32 "Jclass Tracksperanto::Export::SynthEyes < Tracksperanto::Export::Base ", include Tracksperanto::UVCoordinates " " STATUS_KF = 30 " STATUS_STD = 7 " STATUS_REENABLE = 15 " "# def self.desc_and_extension " "syntheyes_2dt.txt" " end " " def self.human_name "+ "Syntheyes 2D tracker paths file" " end " "0 def start_export( img_width, img_height) "1 @width, @height = img_width, img_height " end " "/ def start_tracker_segment(tracker_name) "C @last_registered_frame, @tracker_name = nil, tracker_name " end " "I def export_point(frame, abs_float_x, abs_float_y, float_residual) "e values = [@tracker_name, frame] + absolute_to_uv(abs_float_x, abs_float_y, @width, @height) "+ values << get_outcome_code(frame) "1 @io.puts("%s %d %.6f %.6f %d" % values) " end " " private "d # It's very important that we provide an outcome code for Syntheyes. Regular keyframes get "k # STATUS_STD, and after a gap we have to signal STATUS_REENABLE, otherwise this might bust solves "% def get_outcome_code(frame) "4 outcome = if @last_registered_frame.nil? " STATUS_KF "7 elsif @last_registered_frame != (frame - 1) " STATUS_REENABLE " else " STATUS_STD " end "* @last_registered_frame = frame " outcome " end " end ;[8iiiiiiiiiiiiiiiiiiiiiiiiiiiiiii,iQi+i+iiiiii,iwiiMiii'ii+i+ii;[8;;;;;;;TT;TTT;TTT;TTT;TTT;TTT;TTTT;;T;;TTTTT;T;TT;;"lib/export/nuke_script.rb{;[m"5# Export each tracker as a single Tracker3 node "Kclass Tracksperanto::Export::NukeScript < Tracksperanto::Export::Base " NODE_TEMPLATE = %[ "Tracker3 { " track1 {%s} " name %s " xpos 0 " ypos %d "} "] ". KEYFRAME_PRECISION_TEMPLATE = "%.4f" " PREAMBLE = %[ "version 5.1200 " Root { " inputs 0 " frame 1 " last_frame %d "} "Constant { " inputs 0 " channels rgb "! format "%d %d 0 0 %d %d 1" " name CompSize_%dx%d " postage_stamp false " xpos 0 " ypos -60 " }] " SCHEMATIC_OFFSET = 30 " class T < Array " attr_accessor :name ". include ::Tracksperanto::BlockInit " end " "% def self.desc_and_extension " "nuke.nk" " end " " def self.human_name " "Nuke .nk script" " end " " def start_export(w, h) "2 @max_frame, @ypos, @w, @h = 0, 0, w, h "S # At the start of the file we need to provide the length of the script. "] # We allocate an IO for the file being output that will contain all the trackers, "_ # and then write that one into the script preceded by the preamble that sets length "6 # based on the last frame position in time "6 @trackers_io = Tracksperanto::BufferIO.new " end " "G # We accumulate a tracker and on end dump it out in one piece "1 def start_tracker_segment(tracker_name) "( # Setup for the next tracker "3 @tracker = T.new(:name => tracker_name) " end " "! def end_tracker_segment " @trackers_io.puts( "h NODE_TEMPLATE % [curves_from_tuples(@tracker), @tracker.name, (@ypos += SCHEMATIC_OFFSET)] " ) " end " "K def export_point(frame, abs_float_x, abs_float_y, float_residual) "& # Nuke uses 1-based frames "= @tracker << [frame + 1, abs_float_x, abs_float_y] "4 @max_frame = frame if frame > @max_frame " end " " def end_export " @trackers_io.rewind "F preamble_values = [@max_frame + 1, @w, @h, @w, @h, @w, @h] "0 @io.puts(PREAMBLE % preamble_values) "@ @io.write(@trackers_io.read) until @trackers_io.eof? " @trackers_io.close! " end " " private " "_ # Generates a couple of Nuke curves (x and y) from the passed tuples of [frame, x, y] "( def curves_from_tuples(tuples) "U x_values, y_values, last_frame_exported, repeat_jump = [], [], nil, false " tuples.each do | t | " f = t.shift " "= if last_frame_exported != (f - 1) # new section "# x_values << "x#{f}" "# y_values << "x#{f}" "" repeat_jump = true " elsif repeat_jump "D # If we are AFTER a gap inject another "jump" signal "I # so that Nuke does not animate with gaps but with frames "# x_values << "x#{f}" "# y_values << "x#{f}" "# repeat_jump = false " end " ": t.map!{|e| KEYFRAME_PRECISION_TEMPLATE % e } "! x_values << t.shift "! y_values << t.shift "% last_frame_exported = f " end "` st = [x_values.join(" "), y_values.join(" ")].map{|e| "{curve i %s}" % e }.join(" ") " end "end;[miiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii iiiiiiiiiiiiiiiiiiii,iiQiQiiii iiiiiiiiii i ii/ii~i i i i)iii i i iii~i/i/i/iiii;[m;TT;;;;;;;TT;;;;;;;;;;;;;;;TTTT;;TTT;TTT;TT;;;;T;;;T;TT;TTT;T;T;TT;;TTTTTT;;T;;TTTT;TTTTT;;TTT;;TTTT;T;;"lib/export/base.rb{;[="`# Base exporter. Inherit from this class to automatically register another export format. "^# The exporters in Tracksperanto are event-driven and follow the same conventions - your "d# exporter will be notified when a tracker will be exported and when a tracker has been passed ")# (the last keyframe has been sent) "'class Tracksperanto::Export::Base "( include Tracksperanto::ConstName "+ include Tracksperanto::SimpleExport " " attr_reader :io " " def self.inherited(by) "' Tracksperanto.exporters << by " super " end " "g # Should return the suffix and extension of this export file (like "flame.stabilizer"), without "s # the leading underscore. It's a class method because it gets requested before the exporter is instantiated "# def self.desc_and_extension " "data.txt" " end " "q # Should return the human-readable (read: properly capitalized and with spaces) name of the export module " def self.human_name "" "Abstract export format" " end " "h # The constructor for an exporter should accept a handle to the IO object that you can write to. "U # This gets assigned to @io ivar by default, but you can do whatever you wish "u # By convention, the caller owns the IO handle and will close it when you are done, so don't close t yourself "# def initialize(write_to_io) " @io = write_to_io " end " "i # Called on export start. Will receive the width and height of the comp being exported as Fixnums "0 def start_export( img_width, img_height) " end " "w # Called on export end. By convention, the caller will close the main IO when you are done so don't do it here. "t # However if you've allocated anything during export (like some Tempfiles) here will be the place to get rid " # of them " def end_export " end " "[ # Called on tracker start, once for each tracker. Receives the name of the tracker. "/ def start_tracker_segment(tracker_name) " end " "6 # Called on tracker end, once for each tracker " def end_tracker_segment " end " "j # Called for each tracker keyframe, with the Tracksperanto internal coordinates and frame numbers. "S # The calls come after start_tracker_segment and before end_tracker_segment "N def export_point(at_frame_i, abs_float_x, abs_float_y, float_residual) " end "end;[=iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii iiiiiii iiiiiii i iiiiii;[=;;;;TTT;T;TTT;;;;TFF;;TFF;;;;TTT;;TT;;;;TT;;T;;;TT;;;T;;"$lib/tracksperanto/zip_tuples.rb{;[".# Implements the zip_curve_tuples method "%module Tracksperanto::ZipTuples "O # Zip arrays of "value at" tuples into an array of "values at" tuples. "@ # The first value of each tuple will be the frame number "[ # and keyframes which are not present in all arrays will be discarded. For example: " # "O # zip_curve_tuples( [[0, 12], [1, 23]], [[1, 12]]) #=> [[1, 23, 12]] " # "c # We make use of the fact that setting an offset index in an array fills it with nils up to " # the index inserted "% def zip_curve_tuples(*curves) "I tuples = curves.inject([]) do | tuples, curve_of_at_and_value | ": curve_of_at_and_value.each do | frame, value | "V tuples[frame] = tuples[frame] ? (tuples[frame] << value) : [frame, value] " end " tuples " end " "H tuples.reject{|e| e.nil? || (e.length < (curves.length + 1)) } " end "end;[iiiiiiiiiiiiiiZiiiii2ii;[;T;;;;;;;;TTTT;T;;T;;"'lib/tracksperanto/simple_export.rb{;["j# Implements just_export for quickly pushing trackers out through an exporter without using Pipeline "# plumbing "(module Tracksperanto::SimpleExport "= # Acccepts an array of Tracker objects and comp size. "F # Before calling this, initialize the exporter with the proper " # IO handle "@ def just_export(trackers_array, comp_width, comp_height) "/ start_export(comp_width, comp_height) "& trackers_array.each do | t | ") start_tracker_segment(t.name) " t.each do | kf | "E export_point(kf.frame, kf.abs_x, kf.abs_y, kf.residual) " end " end_tracker_segment " end " end_export " end " end " " ;[iiiiiiiiiiiiiiiiiiii;[;;T;;;TTTTTT;T;T;;;;"lib/import/equalizer3.rb{;[;"4# Imports 3D Equalizer's text files, version 3 "Kclass Tracksperanto::Import::Equalizer3 < Tracksperanto::Import::Base " " def self.human_name "$ "3DE v3 point export file" " end " "" def self.autodetects_size? " true " end " " def parse(passed_io) "2 io = Tracksperanto::ExtIO.new(passed_io) " " detect_format!(io) " extract_trackers(io) " end " " private " " def detect_format!(io) "* first_line = io.gets_non_empty "N self.width, self.height = first_line.scan(/(\d+) x (\d+)/).flatten " end " "" def extract_trackers(io) " ts = [] "# while line = io.gets do "0 if line =~ /^(\w+)/ # Tracker name "/ discard_last_empty_tracker!(ts) "H ts.push(Tracksperanto::Tracker.new(:name => line.strip)) "B report_progress("Capturing tracker #{line.strip}") "! elsif line =~ /^\t/ "0 ts[-1].push(make_keyframe(line)) " end " end " "+ discard_last_empty_tracker!(ts) " ts " end " "3 def discard_last_empty_tracker!(in_array) "5 if (in_array.any? && in_array[-1].empty?) "$ in_array.delete_at(-1) "T report_progress("Removing the last tracker since it had no keyframes") " end " end " "& def make_keyframe(from_line) ") frame, x, y = from_line.split ": report_progress("Capturing keyframe #{frame}") "] Tracksperanto::Keyframe.new(:frame => (frame.to_i - 1), :abs_x => x, :abs_y => y) " end "end;[;iiiiiiiiiiiiiiiiiiiiiiiiiiiii4i8i8i8igifiiiiiiii:iiiii9iigiifiii;[;;T;TTT;TTT;TT;TT;;T;TTT;;TTTTTTTTT;;;TT;;TTTT;T;TTTT;;"lib/export/shake_text.rb{;[!"+# Export for Shake .txt tracker blobs "Jclass Tracksperanto::Export::ShakeText < Tracksperanto::Export::Base "W PREAMBLE = "TrackName %s\n Frame X Y Correlation\n" " POSTAMBLE = "\n" "1 TEMPLATE = " %.2f %.3f %.3f %.3f" " "# def self.desc_and_extension " "shake_trackers.txt" " end " " def self.human_name ") "Shake trackers in a .txt file" " end " "/ def start_tracker_segment(tracker_name) "* @io.puts PREAMBLE % tracker_name " end " " def end_tracker_segment " @io.puts POSTAMBLE " end " "I def export_point(frame, abs_float_x, abs_float_y, float_residual) ", # Shake starts from frame 1, not 0 "U line = TEMPLATE % [frame + 1, abs_float_x, abs_float_y, 1 - float_residual] " @io.puts line " end "end;[!iiiiiiiiiiiiiiiiiiiiiii,iiQi+ii;[!;TTTT;TTT;TTT;TTT;TTT;T;TT;;"#lib/tracksperanto/returning.rb{;[ "%module Tracksperanto::Returning ": # The "returning" idiomn copied from ActiveSupport " def returning(r) " yield(r); r " end "end;[ iiii}ii;[ T;TT;;"(lib/tracksperanto/progressive_io.rb{;[Q"q# Used for IO objects that need to report the current offset at each operation that changes the said offset "P# (useful for building progress bars that report on a file read operation) " frame - 1, :abs_x => x, :abs_y => y) " end " "N Tracksperanto::Tracker.new(:name => name, :keyframes => keyframes) " end " "t def collect_tracker(name, x_curve, y_curve, corr_curve, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12) "? unless x_curve.is_a?(Array) && y_curve.is_a?(Array) "p report_progress("Tracker #{name} had no anim or unsupported interpolation and can't be recovered") " return " end " "9 report_progress("Scavenging tracker #{name}") " "D curve_set = combine_curves(x_curve, y_curve, corr_curve) " "S keyframes = zip_curve_tuples(*curve_set).map do | (frame, x, y, corr) | "v Tracksperanto::Keyframe.new(:frame => frame - 1, :abs_x => x, :abs_y => y, :residual => (1 - corr.to_f)) " end " "N Tracksperanto::Tracker.new(:name => name, :keyframes => keyframes) " end " ". def combine_curves(x, y, corr_curve) " curve_set = [x, y] "i curve_set << corr_curve if (corr_curve.respond_to?(:length) && corr_curve.length >= x.length) " curve_set " end " end " " def parse(script_io) " trackers = [] "= progress_proc = lambda{|msg| report_progress(msg) } "? Traxtractor.new(script_io, [trackers, progress_proc]) " trackers " end " "end;[iiiiiiiiiiiiiiiiiiiiiiiCiCiKiKiiiiii0i^imiiiiiiiii i i.iiiiiiiiiiiiiiiiii>iiui=iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii iiii iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiTiii>iui=iiuii>i>i>iiiiieiii'iiiiii?iiiiii=ii=ii=i)iii=iii>iuii=iiiiiiiiiii;[TTT;TFF;TTT;;;TT;;;TTTTTTT;;;;;TTT;TT;;;;;TTT;;;;;;;;;;;;;;;;;T;TT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;T;T;TTTT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;T;T;TTTT;;;T;TTT;TTT;TTTTTFF;TT;TT;;T;;TTTT;;T;T;TT;;T;;TTTT;;;TTTTT;;;"lib/import/maya_live.rb{;[>"Iclass Tracksperanto::Import::MayaLive < Tracksperanto::Import::Base " "S # Maya Live exports and imports tracks in "aspect units", so a point at 0,0 "T # will be at -1.78,-1 in MayaLive coordinates with aspect of 1.78. Therefore "[ # we offer an override for the aspect being imported (if the pixels are not square) " attr_accessor :aspect " " def self.human_name "' "Maya Live track export file" " end " "" def self.autodetects_size? " true " end " " COMMENT = /^# / " " def parse(original_io) "4 io = Tracksperanto::ExtIO.new(original_io) "< extract_width_height_and_aspect(io.gets_non_empty) " trackers = [] " "( while line = io.gets_and_strip " if line =~ COMMENT "d trackers << Tracksperanto::Tracker.new(:name => line.gsub(/#{COMMENT} Name(\s+)/, '')) " next " end " "; tracker_num, frame, x, y, residual = line.split " "8 abs_x, abs_y = aspect_values_to_pixels(x, y) "z trackers[-1].keyframe! :frame => frame, :abs_x => abs_x, :abs_y => abs_y, :residual => set_residual(residual) " end " " trackers " end " " private " "+ def aspect_values_to_pixels(x, y) " [ "< (@width.to_f / 2.0) + (x.to_f * @x_unit.to_f), "< (@height.to_f / 2.0) + (y.to_f * @y_unit.to_f) " ] " end " "7 def extract_width_height_and_aspect(from_str) ": self.width, self.height = from_str.scan(/\d+/) "0 @aspect ||= width.to_f / height.to_f "+ @x_unit = width / (@aspect * 2) "& @y_unit = height / (1 * 2) " end " "$ def set_residual(residual) "- (residual == "-1" ? 0 : residual) " end "end;[>iiiiiiiiiiiiiiiiiii iiiii|i)i)iii ii i iiiiiiii ii"i4ii iii ii i iii i4i i;[>T;;;;T;TTT;TTT;T;TTTT;TTTT;;T;TT;;T;;T;T;TT;T;TTTTT;;TTT;"lib/export/pftrack_5.rb{;["3# Export for PFTrack .2dt files for version 5 "Mclass Tracksperanto::Export::PFTrack5 < Tracksperanto::Export::PFTrack4 " "% def self.desc_and_extension " "pftrack_v5.2dt" " end " " def self.human_name "" "PFTrack v5 .2dt file" " end " "! def end_tracker_segment " @io.write("\n\n") "8 @io.puts(@tracker_name.inspect) # autoquotes "R @io.puts("Primary".inspect) # For primary/secondary cam in stereo pair "" @io.puts(@frame_count) " " @tracker_io.rewind "> @io.write(@tracker_io.read) until @tracker_io.eof? " @tracker_io.close! " end "end;[iiiiiiiiiiiii iiiiiiiii;[;T;TTT;TTT;TTTTT;TTT;;"lib/export/pftrack.rb{;[)"%# Export for PFTrack .2dt files "Iclass Tracksperanto::Export::PFTrack4 < Tracksperanto::Export::Base " "1 KEYFRAME_TEMPLATE = "%s %.3f %.3f %.3f" " "% def self.desc_and_extension " "pftrack_v4.2dt" " end " " def self.human_name "" "PFTrack v4 .2dt file" " end " "1 def start_tracker_segment(tracker_name) "( # Setup for the next tracker " @frame_count = 0 "( @tracker_name = tracker_name "5 @tracker_io = Tracksperanto::BufferIO.new " end " "! def end_tracker_segment " @io.write("\n\n") "8 @io.puts(@tracker_name.inspect) # autoquotes "" @io.puts(@frame_count) " " @tracker_io.rewind "> @io.write(@tracker_io.read) until @tracker_io.eof? " @tracker_io.close! " end " "K def export_point(frame, abs_float_x, abs_float_y, float_residual) " @frame_count += 1 "\ line = KEYFRAME_TEMPLATE % [frame, abs_float_x, abs_float_y, float_residual / 8] "" @tracker_io.puts(line) " end "end;[)iiiiiiiiiiiiii ii i i iiii iiiiiiiiiRiiQiQii;[);T;T;TTT;TTT;T;TTT;;TTTT;TTT;;TTTT;;"#lib/export/flame_stabilizer.rb{;["/# TODO: this exporter is MAJORLY slow now "Pclass Tracksperanto::Export::FlameStabilizer < Tracksperanto::Export::Base " " COLOR = "50 50 50" "0 DATETIME_FORMAT = '%a %b %d %H:%M:%S %Y' " "# def self.desc_and_extension " "flame.stabilizer" " end " " def self.human_name "+ "Flame/Smoke 2D Stabilizer setup" " end " "0 def start_export( img_width, img_height) " @counter = 0 "1 @width, @height = img_width, img_height "- @temp = Tracksperanto::BufferIO.new ": @writer = Tracksperanto::FlameBuilder.new(@temp) " end " " def end_export "> # Now make another writer, this time for our main IO "8 @writer = Tracksperanto::FlameBuilder.new(@io) " "L # Now we know how many trackers we have so we can write the header "& # data along with NbTrackers "8 write_header_with_number_of_trackers(@counter) " "M # Now write everything that we accumulated earlier into the base IO " @temp.rewind "0 @io.write(@temp.read) until @temp.eof? " @temp.close! " "= # Send the ChannelEnd command and list the trackers " @writer.channel_end " @counter.times do |i| "B @writer.write_unterminated_block!("tracker", i) do |t| " t.active true "0 t.color_hash!("colour", 0, 100, 0) " t.fixed_ref true " t.fixed_x false " t.fixed_y false " t.tolerance 100 " end " end " end " "/ def start_tracker_segment(tracker_name) " @counter += 1 "# @write_first_frame = true " end " "I def export_point(frame, abs_float_x, abs_float_y, float_residual) "! flame_frame = frame + 1 " if @write_first_frame "E export_first_point(flame_frame, abs_float_x, abs_float_y) "& @write_first_frame = false " else "I export_remaining_point(flame_frame, abs_float_x, abs_float_y) " end " end " " def end_tracker_segment "J # We write these at tracker end since we need to know in advance "2 # how many keyframes they should contain "9 write_shift_channel("shift/x", @x_shift_values) "9 write_shift_channel("shift/y", @y_shift_values) " end " " private " "I def export_remaining_point(flame_frame, abs_float_x, abs_float_y) "Z # Just continue buffering the upcoming shift keyframes and flush them in the end "I shift_x, shift_y = @base_x - abs_float_x, @base_y - abs_float_y "6 @x_shift_values.push([flame_frame, shift_x]) "6 @y_shift_values.push([flame_frame, shift_y]) " end " "E def export_first_point(flame_frame, abs_float_x, abs_float_y) "5 @base_x, @base_y = abs_float_x, abs_float_y "5 write_first_frame(abs_float_x, abs_float_y) "K # For Flame to recognize the reference frame of the Shift channel "T # we need it to contain zero as an int, not as a float. The shift proceeds " # from there. ". @x_shift_values = [[flame_frame, 0]] ". @y_shift_values = [[flame_frame, 0]] " end " "L # The shift channel is what determines how the tracking point moves. "< def write_shift_channel(name_without_prefix, values) "? @writer.channel(prefix(name_without_prefix)) do | c | "% c.extrapolation :constant " c.value values[0][1] " c.key_version 1 " c.size values.length "3 values.each_with_index do | (f, v), i | " c.key(i) do | k | " k.frame f " k.value v "' k.interpolation :linear " k.left_slope 2.4 "! k.right_slope 2.4 " end " end " end " end " "# def prefix(tracker_channel) "; ["tracker#{@counter}", tracker_channel].join("/") " end " "D def write_header_with_number_of_trackers(number_of_trackers) "/ @writer.stabilizer_file_version "5.0" "C @writer.creation_date(Time.now.strftime(DATETIME_FORMAT)) " @writer.linebreak!(2) " "0 @writer.nb_trackers number_of_trackers " @writer.selected 0 "$ @writer.frame_width @width "& @writer.frame_height @height " @writer.auto_key true "" @writer.motion_path true " @writer.icons true "+ @writer.auto_pan false # hate it! " @writer.edit_mode 0 " @writer.format 0 "3 @writer.color_hash!("padding", 0, 100, 0) "$ @writer.oversampling false " @writer.opacity 50 " @writer.zoom 3 " @writer.field false " @writer.backward false " @writer.anim " end " "# def write_first_frame(x, y) " write_track_channels "& write_track_width_and_height "$ write_ref_width_and_height "" write_ref_channels(x, y) "* write_deltax_and_deltay_channels " write_offset_channels " end " " def write_track_channels "0 ctr_x, ctr_y = @width / 2, @height / 2 " "S # track determines where the tracking box is, and should be in the center "G # of the image for Flame to compute all other shifts properly "c %w( track/x track/y).map(&method(:prefix)).zip([ctr_x, ctr_y]).each do | cname, default | "+ @writer.channel(cname) do | c | ") c.extrapolation("constant") "# c.value(default.to_i) " c.colour(COLOR) " end " end " end " "' # The size of the tracking area "( def write_track_width_and_height "W %w( track/width track/height ).map(&method(:prefix)).each do | channel_name | "2 @writer.channel(channel_name) do | c | "% c.extrapolation :linear " c.value 15 " c.colour COLOR " end " end " end " "( # The size of the reference area "& def write_ref_width_and_height "R %w( ref/width ref/height).map(&method(:prefix)).each do | channel_name | "2 @writer.channel(channel_name) do | c | "% c.extrapolation :linear " c.value 10 " c.colour COLOR " end " end " end " "R # The Ref channel contains the reference for the shift channel in absolute "Q # coordinates, and is set as float. Since we do not "snap" the tracker in "Q # the process it's enough for us to make one keyframe in the ref channels "7 # at the same frame as the first shift keyframe ", def write_ref_channels(ref_x, ref_y) "_ %w( ref/x ref/y).map(&method(:prefix)).zip([ref_x, ref_y]).each do | cname, default | "+ @writer.channel(cname) do | c | ") c.extrapolation("constant") " c.value(default) " c.colour(COLOR) " c.key_version 1 " c.size 1 " c.key(0) do | k | " k.frame 1 " k.value default ") k.interpolation :constant " k.left_slope 2.4 "! k.right_slope 2.4 " end " end " end " end " ", def write_deltax_and_deltay_channels "A # This is used for deltax and deltay (offset tracking). "& # We set it to zero and lock "F %w( ref/dx ref/dy).map(&method(:prefix)).each do | chan, v | "* @writer.channel(chan) do | c | ") c.extrapolation("constant") " c.value 0 " c.colour(COLOR) " c.size 2 " c.key_version 1 " c.key(0) do | k | " k.frame 0 " k.value 0 "! k.value_lock true "" k.delete_lock true ") k.interpolation :constant " end " c.key(1) do | k | " k.frame 1 " k.value 0 "! k.value_lock true "" k.delete_lock true ") k.interpolation :constant " end " end # Chan block " end " end " "! def write_offset_channels "C %w(offset/x offset/y).map(&method(:prefix)).each do | c | "* @writer.channel(c) do | chan | "* chan.extrapolation :constant " chan.value 0 " end " end " end " " end ;[iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii iiii,iQiQiiii)iiiiiii iiiiii*iiMi)i)iiii iiiiiiiiii i i i i i i iQiQiQiQiQiQiiii ii"i!i!iiiiiiiiiiiiiiiiiiiiiiiiiii iiiiiiiii iiiii i i i iiiiiiii i i i iiiiiiii i i i iiiiiiiiiii i i i i i i i i i i i iiiiiiiiii i i i i i i i i i i i ii i i i i i iiiiiiii i i iiiii;[;T;TT;TTT;TTT;TTTTT;;T;T;;;T;;TTT;;TTTTTTTTT;;;;TTT;;TTTTT;T;;;T;;TT;;T;T;TTT;;TTT;;;TT;;;TTTTTTTTTTTTT;;;T;TTT;TTTT;TTTTTTTTTTTTTTTTT;;TTTTTTT;;TT;;;TTTTT;;;;;TTTTTT;;T;;TTTTTT;;T;;;;;TTTTTTTTTTTTTT;;;T;T;;TTTTTTTTTTTTT;TTTTTT;;;T;TTTTT;;T;T" lib/tracksperanto/safety.rb{;["^# Implements the +safe_reader+ class method which will define (or override) readers that "# raise if ivar is nil ""module Tracksperanto::Safety " def self.included(into) " into.extend(self) " super " end " "- # Inject a reader that checks for nil "$ def safe_reader(*attributes) "( attributes.each do | an_attr | "E alias_method "#{an_attr}_without_nil_protection", an_attr "% define_method(an_attr) do "= val = send("#{an_attr}_without_nil_protection") "N raise "Expected #{an_attr} on #{self} not to be nil" if val.nil? " val " end " end " end "end;[iiiiiiiiiiiiiimimi6iiii;[;;TTTT;;;TTTTTTT;;T;""lib/tracksperanto/keyframe.rb{;[#"u# Internal representation of a keyframe, that carries info on the frame location in the clip, x y and residual. "# "K# Franme numbers are zero-based (frame 0 is first frame of the clip). "_# X-coordinate (abs_x) is absolute pixels from lower left corner of the comp to the right "U# Y-coordinate (abs_y) is absolute pixels from lower left corner of the comp up "U# Residual is how far in pixels the tracker strolls away, and is the inverse of "X# correlation (with total correlation of one the residual excursion becomes zero). "#class Tracksperanto::Keyframe "$ include Tracksperanto::Casts "( include Tracksperanto::BlockInit " "G # Absolute integer frame where this keyframe is placed, 0 based " attr_accessor :frame " "A # Absolute float X value of the point, zero is lower left " attr_accessor :abs_x " "A # Absolute float Y value of the point, zero is lower left " attr_accessor :abs_y " "2 # Absolute float residual (0 is "spot on") " attr_accessor :residual " "/ cast_to_float :abs_x, :abs_y, :residual " cast_to_int :frame " " def inspect "H '#< %.1fx%.1f @%d ~%.2f) >' % [abs_x, abs_y, frame, residual] " end "end;[#iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiΞ;[#;;;;;;;TTT;;T;;T;;T;;T;TT;TTTT"$lib/tracksperanto/const_name.rb{;["%module Tracksperanto::ConstName " module C " def const_name " to_s.split('::').pop " end " end " " def const_name " self.class.const_name " end " " def self.included(into) " into.extend(C) " super " end "end;[iiifieieiiiiiii i iii;[TTTTT;;TTT;TTT;;"lib/middleware/lerp.rb{;[?"o# This middleware adds linearly interpolated keyframes BETWEEN the keyframes passing through the exporter "Mclass Tracksperanto::Middleware::Lerp < Tracksperanto::Middleware::Base " attr_accessor :enabled " " def end_tracker_segment "C @last_f, @last_x, @last_y, @last_res = nil, nil, nil, nil " super " end " "A def export_point(frame, float_x, float_y, float_residual) " "H if @enabled && @last_f && (frame - @last_f > 1) # Interpolate! "$ interpolated_frames = [] " interpolated_x = [] "L lerp(@last_f, @last_x, frame, float_x) do | interp_f, interp_x | "- interpolated_frames << interp_f "( interpolated_x << interp_x " end " " interpolated_y = [] "L lerp(@last_f, @last_y, frame, float_y) do | interp_f, interp_y | "( interpolated_y << interp_y " end " "! interpolated_res = [] "W lerp(@last_f, @last_res, frame, float_residual) do | interp_f, interp_res | ", interpolated_res << interp_res " end " "z tuples = interpolated_frames.zip(interpolated_x).zip(interpolated_y).zip(interpolated_res).map{|e| e.flatten } ") tuples.each do | f, x, y, r | "$ super(f.to_i, x, y, r) " end " else "^ super(frame, float_x + (@x_shift || 0), float_y + (@y_shift || 0), float_residual) " end " "X @last_f, @last_x, @last_y, @last_res = frame, float_x, float_y, float_residual " end " " private "D # Do a simple linear interpolxion. The function will yield "R # the interim X and Y, one tuple per whole value between the set points, "R # and return the last tuple (so you can return-assign from it in a loop) "E def lerp(last_x, last_y, x, y) #:yields: interp_x, interp_y " if last_x.nil? " yield(x, y) " else "# gap_size = x - last_x "; increment = (y.to_f - last_y) / gap_size.to_f "- (1..gap_size).each do | index | "C yield(last_x + index, last_y + (increment * index)) " end " end " " return [x, y] " end "end;[?iiiiii iiii iiiiiiiiiiiiiiiiiiii iiiiiiii iiiiiii iiiii i iiiiiii;[?;TT;TTT;;T;TTTTTT;;TTT;;TTT;;TTT;;T;;T;;T;;;TTF;TTTT;;;T;;"#lib/tracksperanto/buffer_io.rb{;[4"require "tempfile" " "u# BufferIO is used for writing big segments of text. When the segment is bigger than a certain number of bytes, "D# the underlying memory buffer will be swapped with a tempfile "7class Tracksperanto::BufferIO < DelegateClass(IO) "( include Tracksperanto::Returning " "" MAX_IN_MEM_BYTES = 100_000 " " def initialize "" __setobj__(StringIO.new) " end " " def write(s) "> returning(super) { replace_with_tempfile_if_needed } " end " " def puts(s) "> returning(super) { replace_with_tempfile_if_needed } " end " " def <<(s) "> returning(super) { replace_with_tempfile_if_needed } " end " " def putc(c) "> returning(super) { replace_with_tempfile_if_needed } " end " " def close! "+ __getobj__.close! if @tempfile_in " __setobj__(nil) " end " " private " "+ def replace_with_tempfile_if_needed " return if @tempfile_in " io = __getobj__ "& if io.pos > MAX_IN_MEM_BYTES "3 tf = Tempfile.new("tracksperanto-xbuf") " tf.write(io.string) " __setobj__(tf) " @tempfile_in = true " end " end "end;[4iiiiiiiiiiiiiiiiiiiPiiiiiiiiiiii/iiiiiii}iqiZiiiiiii;[4T;;;TT;T;TTT;TTT;TTT;TFF;TFF;TTT;;T;TTTTTTTT;;;"lib/pipeline/base.rb{;["# The base pipeline is the whole process of track conversion from start to finish. The pipeline object organizes the import formats, scans them, "{# applies the default middlewares and yields them for processing. Here's how a calling sequence for a pipeline looks like: "# "2# pipe = Tracksperanto::Pipeline::Base.new "X# pipe.progress_block = lambda{|percent, msg| puts("#{msg}..#{percent.to_i}%") } " # "a# pipe.run("/tmp/shakescript.shk", :width => 720, :height => 576) do | *all_middlewares | "(# # configure middlewares here " # end "# "\# The pipeline will also automatically allocate output files with the right extensions ":# at the same place where the original file resides, ";# and setup outputs for all supported export formats. ")class Tracksperanto::Pipeline::Base "* EXTENSION = /\.([^\.]+)$/ #:nodoc: " "( include Tracksperanto::BlockInit " "t # How many points have been converted. In general, the pipeline does not preserve the parsed tracker objects "' # after they have been exported "% attr_reader :converted_points " "0 # How many keyframes have been converted "( attr_reader :converted_keyframes " "I # A block acepting percent and message vars can be assigned here. "G # When it's assigned, the pipeline will pass the status reports "H # of all the importers and exporters to the block, together with " # percent complete "% attr_accessor :progress_block " "P # Assign an array of exporters to use them instead of the standard ones " attr_accessor :exporters " "i DEFAULT_OPTIONS = {:width => 720, :height => 576, :parser => Tracksperanto::Import::ShakeScript } " "J # Contains arrays of the form ["MiddewareName", {:param => value}] "( attr_accessor :middleware_tuples " "0 def wrap_output_with_middlewares(output) "L return output unless (middleware_tuples && middleware_tuples.any?) " "] middleware_tuples.reverse.inject(output) do | wrapped, (middleware_name, options) | "U Tracksperanto.get_middleware(middleware_name).new(wrapped, options || {}) " end " end " "@ # Runs the whole pipeline. Accepts the following options "[ # * width - The comp width, for the case that the format does not support auto size "] # * height - The comp height, for the case that the format does not support auto size "d # * parser - The parser class, for the case that it can't be autodetected from the file name "U def run(from_input_file_path, passed_options = {}) #:yields: *all_middlewares "3 o = DEFAULT_OPTIONS.merge(passed_options) " " # Reset stats "8 @converted_keyframes, @converted_points = 0, 0 " " # Assign the parser "W importer = initialize_importer_with_path_and_options(from_input_file_path, o) " " # Open the file "; read_data = File.open(from_input_file_path, "rb") " " # Setup a multiplexer "7 mux = setup_outputs_for(from_input_file_path) " " # Setup middlewares "6 endpoint = wrap_output_with_middlewares(mux) "] @converted_points, @converted_keyframes = run_export(read_data, importer, endpoint) " end " "6 def report_progress(percent_complete, message) "L @progress_block.call(percent_complete, message) if @progress_block " end " "T def initialize_importer_with_path_and_options(from_input_file_path, options) "E d = Tracksperanto::FormatDetector.new(from_input_file_path) "% if d.match? && d.auto_size? " d.importer_klass.new " elsif d.match? "( require_dimensions_in!(opts) "R d.importer_klass.new(:width => opts[:width], :height => opts[:height]) " else "s raise "Cannot autodetect the file format - please specify the importer explicitly" unless opts[:parser] "= klass = Tracksperanto.get_exporter(opts[:parser]) "G require_dimensions_in!(opts) unless klass.autodetects_size? "G klass.new(:width => opts[:width], :height => opts[:height]) " end " end " "( def require_dimensions_in!(opts) "l raise "Width and height must be provided for this importer" unless (opts[:width] && opts[:height]) " end " "S # Runs the export and returns the number of points and keyframes processed. "X # If a block is passed, the block will receive the percent complete and the last "9 # status message that you can pass back to the UI "9 def run_export(tracker_data_io, parser, exporter) "9 points, keyframes, percent_complete = 0, 0, 0.0 " "B report_progress(percent_complete, "Starting the parser") " "+ # Report progress from the parser "W parser.progress_block = lambda { | m | report_progress(percent_complete, m) } " "_ # Wrap the input in a progressive IO, setup a lambda that will spy on the reader and "] # update the percentage. We will only broadcast messages that come from the parser "7 # though (complementing it with a percentage) "f io_with_progress = Tracksperanto::ProgressiveIO.new(tracker_data_io) do | offset, of_total | "9 percent_complete = (50.0 / of_total) * offset " end "" @ios << io_with_progress " "3 trackers = parser.parse(io_with_progress) " "e report_progress(percent_complete = 50.0, "Validating #{trackers.length} imported trackers") " "& validate_trackers!(trackers) " "> report_progress(percent_complete, "Starting export") " "L percent_per_tracker = (100.0 - percent_complete) / trackers.length " "B # Use the width and height provided by the parser itself "< exporter.start_export(parser.width, parser.height) "8 trackers.each_with_index do | t, tracker_idx | "@ kf_weight = percent_per_tracker / t.keyframes.length " points += 1 "2 exporter.start_tracker_segment(t.name) ", t.each_with_index do | kf, idx | " keyframes += 1 "N exporter.export_point(kf.frame, kf.abs_x, kf.abs_y, kf.residual) " report_progress(percent_complete += kf_weight, "Writing keyframe #{idx+1} of #{t.name.inspect}, #{trackers.length - tracker_idx} trackers to go") " end "( exporter.end_tracker_segment " end " exporter.end_export " "U report_progress(100.0, "Wrote #{points} points and #{keyframes} keyframes") " " [points, keyframes] " ensure "[ @ios.reject!{|e| e.close unless (!e.respond_to?(:closed?) || e.closed?) } if @ios " end " "7 # Setup output files and return a single output "% # that replays to all of them ". def setup_outputs_for(input_file_path) "H file_name = File.basename(input_file_path).gsub(EXTENSION, '') "C export_klasses = exporters || Tracksperanto.exporters ") Tracksperanto::Export::Mux.new( "4 export_klasses.map do | exporter_class | "T export_name = [file_name, exporter_class.desc_and_extension].join("_") "Q export_path = File.join(File.dirname(input_file_path), export_name) "P exporter = exporter_class.new(open_owned_export_file(export_path)) " end " ) " end " "N # Open the file for writing and register it to be closed automatically "0 def open_owned_export_file(path_to_file) " @ios ||= [] "0 handle = File.open(path_to_file, "wb") " @ios << handle " handle " end " "= # Check that the trackers made by the parser are A-OK "( def validate_trackers!(trackers) ") trackers.reject!{|t| t.empty? } "x raise "Could not recover any non-empty trackers from this file. Wrong import format maybe?" if trackers.empty? " end "end;[iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii;[;;;;;;;;;;;;;TT;T;;;T;;T;;;;;T;;T;T;;T;TFFFFFF;;;;;TFFFFFFFFFFFFFFFFFF;TFF;TFFFFFFFFFFFFF;TFF;;;;TFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;;;TFFFFFFFFFF;;TFFFFF;;TFFFF"lib/import/equalizer4.rb{;[3")# Imports 3D Equalizer's text files "Kclass Tracksperanto::Import::Equalizer4 < Tracksperanto::Import::Base " " def self.human_name "$ "3DE v4 point export file" " end " " " def parse(passed_io) " ts = [] "2 io = Tracksperanto::ExtIO.new(passed_io) " "* num_t = detect_num_of_points(io) "3 num_t.times { ts << extract_tracker(io) } " " ts " end " " private " "& def detect_num_of_points(io) "" io.gets_non_empty.to_i " end " "9 KF_PATTERN = /^(\d+)\s([\-\d\.]+)\s([\-\d\.]+)/ "! def extract_tracker(io) "B t = Tracksperanto::Tracker.new(:name => io.gets.strip) " ": report_progress("Capturing tracker #{t.name}") " "C io.gets # Tracker color, internal 3DE repr and 0 is Red " "+ num_of_keyframes = io.gets.to_i " catch(:__emp) do "' num_of_keyframes.times do "( line = io.gets_non_empty "( throw :__emp unless line " "; frame, x, y = line.scan(KF_PATTERN).flatten "> report_progress("Capturing keyframe #{frame}") "Q t.keyframe!(:frame => (frame.to_i - 1), :abs_x => x, :abs_y => y) " end " end " t " end "end;[3iiiiiiiiiiiiiiiiiiiiiiiiiiiii ii ii i i iiziiiiziii ii;[3;T;TTT;;TTT;TT;T;;T;TTT;TTT;T;T;TTTTT;TTT;;T;;