tracksperanto 1.8.4 → 1.9.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,9 @@
1
+ === 1.9.0 / 2010-03-21
2
+
3
+ * Use proper progress bars
4
+ * Allow proper chaining of middlewares in the commandline app - you can now apply many middlewares of the same nature in chainn, and they will be ordered correctly
5
+ * Small rewrites to Pipeline::Base (changes API slightly - please consult the docs)
6
+
1
7
  === 1.8.4 / 2010-03-10
2
8
 
3
9
  * Fix PFTrack imports neglecting the feature name if it only contained digits
data/README.txt CHANGED
@@ -88,24 +88,42 @@ Middlewares. Any processing chain (called a Pipeline) usually works like this:
88
88
  * Trackers and their keyframes are dumped to all export
89
89
  formats Tracksperanto supports, optionally passing through Middleware
90
90
 
91
- == Importing your own formats
91
+
92
+ == Limitations
93
+
94
+ Information about the search area, reference area and offset is not passed along (outside
95
+ of scope for the app and different trackers handle these differently, if at all). For some
96
+ modules no residual will be passed along (3D tracking apps generally do not export residual
97
+ with backprojected 3D features).
98
+
99
+ == Extending Tracksperanto
100
+
101
+ === Importing your own formats
92
102
 
93
103
  You can easily write a Tracksperanto import module - refer to Tracksperanto::Import::Base
94
- docs. Your importer should return an array of Tracksperanto::Tracker objects which
104
+ docs. Your importer will be configured with width and height of the comp that it is importing, and will get an IO
105
+ object with the file that you are processing. The parse method should then return an array of Tracksperanto::Tracker objects which
95
106
  are themselves arrays of Tracksperanto::Keyframe objects.
96
107
 
97
- == Exporting your own formats
108
+ === Exporting your own formats
98
109
 
99
- You can easily write an exporter. Refer to the Tracksperanto::Export::Base docs.
110
+ You can easily write an exporter. Refer to the Tracksperanto::Export::Base docs. Note that your exporter should be able to chew alot of data
111
+ (hundreds of trackers with thousands of keyframes with exported files growing up to 5-10 megs in size are not uncommon!). This means that
112
+ the exporter should work with streams (smaller parts of the file being exported will be held in memory at a time).
100
113
 
101
- == Ading your own processing steps
114
+ === Ading your own processing steps
102
115
 
103
- You probably want to write a Middleware (consult the Tracksperanto::Middleware::Base docs). A Middleware is just like
104
- an exporter, except that it wraps another exporter instead (so you can intercept export calls and massage the data as it is moving through).
116
+ You probably want to write a Middleware (consult the Tracksperanto::Middleware::Base docs) if you need some processing applied to the tracks
117
+ or their data. A Middleware is just like an export module, except that instead it sits between the exporter and the exporting routine. Middlewares wrap export
118
+ modules or each other, so you can stack different middleware modules together (like "scale first, then move").
105
119
 
106
- == Limitations
120
+ === Writing your own processing routines
107
121
 
108
- Information about the search area, reference area and offset is not passed along (outside
109
- of scope for the app and different trackers handle these differently, if at all). For some
110
- modules no residual will be passed along (3D tracking apps generally do not export residual
111
- with backprojected 3D features).
122
+ You probably want to write a descendant of Tracksperanto::Pipeline::Base. This is a class that manages a conversion from start to finish, including detecting the
123
+ input format, allocating output files and building a chain of Middlewares to process the export. If you want to make a GUI for Tracksperanto you will likely need
124
+ to write your own Pipeline class or reimplement parts of it.
125
+
126
+ === Reporting status from long-running operations
127
+
128
+ Almost every module in Tracksperanto has a method called report_progress. This method is used to notify an external callback of what you are doing, and helps keep
129
+ the software user-friendly. A well-behaved Tracksperanto module should manage it's progress reports properly.
data/Rakefile CHANGED
@@ -10,6 +10,7 @@ begin
10
10
  Hoe.spec('tracksperanto') do | p |
11
11
  p.clean_globs = %w( **/.DS_Store )
12
12
  p.version = Tracksperanto::VERSION
13
+ p.extra_deps = {"progressbar" => ">=0"}
13
14
  p.extra_dev_deps = {"flexmock" => ">=0"}
14
15
  p.rubyforge_name = 'guerilla-di'
15
16
  p.developer('Julik Tarkhanov', 'me@julik.nl')
data/bin/tracksperanto CHANGED
@@ -12,23 +12,16 @@
12
12
 
13
13
  require File.dirname(__FILE__) + '/../lib/tracksperanto'
14
14
  require 'optparse'
15
+ require 'rubygems'
16
+ require 'progressbar'
15
17
 
16
18
  # Sane defaults
17
19
  reader_klass = nil
18
20
  width = nil
19
21
  height = nil
20
- reformat_w = nil
21
- reformat_h = nil
22
- scale_x = 1.0
23
- scale_y = 1.0
24
- slip = 0
25
- golden_tracks = false
26
- x_shift = 0
27
- y_shift = 0
28
- length_gate = 0
29
- set_prefix = ''
22
+
23
+ middlewares = []
30
24
  sole_format = nil
31
- lerp = false
32
25
  reader_name = "ShakeScript"
33
26
  readers = Tracksperanto.importer_names
34
27
  writers = Tracksperanto.exporter_names
@@ -42,34 +35,52 @@ parser = OptionParser.new do | p |
42
35
  "Use the specific import translator (will try to autodetect, but can be #{Tracksperanto.importer_names.join(', ')})") do | f |
43
36
  reader_name = f
44
37
  end
45
- p.on(" -p", "--prefix PREFIX", String, "A prefix to prepend to tracker names in bulk") { |w| set_prefix = w }
46
38
  p.on(" -w", "--width WIDTH_IN_PIXELS", Integer, "Absolute input comp width in pixels (will try to autodetect)") { |w| width = w }
47
39
  p.on(" -h", "--height HEIGHT_IN_PIXELS", Integer, "Absolute input comp height in pixels (will try to autodetect)") {|w| height = w }
48
- p.on(" -xs", "--xscale X_SCALING_FACTOR", Float, "Scale the result in X by this factor (1.0 is the default)") {|sx| scale_x = sx }
49
- p.on(" -ys", "--yscale Y_SCALING_FACTOR", Float, "Scale the result in Y by this factor (1.0 is the default)") {|sy| scale_y = sy }
50
- p.on(" -s", "--slip FRAMES", Integer, "Slip the result by this number of frames, positive is 'later'") {|sy| slip = sy }
51
- p.on(" -g", "--golden", "Reset the residuals of all trackers to 0 (ignore correlation)") {|g_flag|
52
- golden_tracks = g_flag
53
- }
54
- p.on(" -rx", "--reformat-x NEW_PIX_WIDTH", Integer, "Reformat the comp to this width and scale all tracks to it") {|rw|
55
- reformat_w = rw
56
- }
57
- p.on(" -ry", "--reformat-y NEW_PIX_HEIGHT", Integer, "Reformat the comp to this height and scale all tracks to it") {|rh|
58
- reformat_h = rh
59
- }
60
- p.on(" -o", "--only EXPORTER_NAME", String, "Only export the selected format, format must be one of #{writers.join(", ")}") {|f|
40
+
41
+ p.on(" -xs", "--xscale X_SCALING_FACTOR", Float, "Scale the result in X by this factor (1.0 is the default)") do |sx|
42
+ middlewares << ["Scaler", {:x_factor => sx}]
43
+ end
44
+ p.on(" -ys", "--yscale Y_SCALING_FACTOR", Float, "Scale the result in Y by this factor (1.0 is the default)") do |sy|
45
+ middlewares << ["Scaler", {:y_factor => sx}]
46
+ end
47
+ p.on(" -s", "--slip FRAMES", Integer, "Slip the result by this number of frames, positive is 'later'") do |sy|
48
+ middlewares << ["Slipper", {:slip => sx}]
49
+ end
50
+ p.on(" -g", "--golden", "Reset the residuals of all trackers to 0 (ignore correlation)") do |g_flag|
51
+ middlewares << ["Golden", {:enabled => true}]
52
+ end
53
+ p.on(" -rx", "--reformat-x NEW_PIX_WIDTH", Integer, "Reformat the comp to this width and scale all tracks to it") do |rw|
54
+ middlewares << ["Reformat", {:width => rw}]
55
+ end
56
+ p.on(" -ry", "--reformat-y NEW_PIX_HEIGHT", Integer, "Reformat the comp to this height and scale all tracks to it") do |rh|
57
+ middlewares << ["Reformat", {:height => rh}]
58
+ end
59
+ p.on(" -m", "--min-length LENGTH_IN_FRAMES", Integer, "Only export trackers having more than X keyframes") do | min_kf |
60
+ middlewares << ["LengthCutoff", {:min_length => min_kf}]
61
+ end
62
+ p.on(" -xm", "--xshift X_IN_PIXELS", Float, "Move the points left or right") do |sx|
63
+ middlewares << ["Shift", {:x_shift => sx}]
64
+ end
65
+ p.on(" -ym", "--yshift Y_IN_PIXELS", Float, "Move the points up or down") do |sx|
66
+ middlewares << ["Shift", {:y_shift => sx}]
67
+ end
68
+ p.on(" -p", "--prefix PREFIX", String, "A prefix to prepend to tracker names in bulk") do |w|
69
+ middlewares << ["Prefix", {:prefix => w}]
70
+ end
71
+
72
+ p.on("--lerp", "Linearly interpolate missing keyframes") do
73
+ middlewares << ["Lerp", {:enabled => true}]
74
+ end
75
+
76
+ p.on(" -o", "--only EXPORTER_NAME", String, "Only export the selected format, format must be one of #{writers.join(", ")}") do |f|
61
77
  sole_format = f
62
- }
63
- p.on(" -m", "--min-length LENGTH_IN_FRAMES", Integer, "Only export trackers having more than X keyframes") {|f|
64
- length_gate = f
65
- }
66
- p.on(" -xm", "--xshift X_IN_PIXELS", Float, "Move the points left or right") {|sx| x_shift = sx }
67
- p.on(" -ym", "--yshift Y_IN_PIXELS", Float, "Move the points up or down") {|sx| y_shift = sx }
68
- p.on("--lerp", "Linearly interpolate missing keyframes") {|v| lerp = true }
78
+ end
79
+
69
80
  p.on("--version", "Show the version and exit") {|v|
70
- puts "#{Tracksperanto::VERSION} running on Ruby #{RUBY_VERSION} on #{RUBY_PLATFORM}"
81
+ puts "Tracksperanto v.#{Tracksperanto::VERSION} running on Ruby #{RUBY_VERSION} on #{RUBY_PLATFORM}"
71
82
  puts "Copyright 2008-#{Time.now.year} by Guerilla-DI (Julik Tarkhanov and contributors)"
72
- puts "For questions and support contact info at guerilla-di.org"
83
+ puts "For questions and support contact info#{64.chr}guerilla-di.org"
73
84
  exit(0)
74
85
  }
75
86
  end
@@ -91,34 +102,12 @@ end
91
102
 
92
103
  input_file = ARGV.pop
93
104
  if !input_file
94
- STDERR.puts "No input file provided - should be the last argument"
105
+ STDERR.puts "No input file provided - should be the last argument. Also use the --help option."
95
106
  exit(-1)
96
107
  end
97
108
 
98
- class Spinner
99
- RING = ['|', '/', '-', '\\', '|', '/', '-', '\\']
100
-
101
- def initialize
102
- @counter, @last_len = 0, 0
103
- end
104
-
105
- def spin(percent, msg)
106
- current = RING[(@counter += 1) % RING.length]
107
- output = " [%s %d%%]" % [current, percent]
108
- STDOUT.write(output + ("\r" * output.length))
109
- STDOUT.flush
110
- @last_len = output.length
111
- end
112
-
113
- def finish(with_message = '')
114
- STDOUT.write("\r" * @last_len.to_i)
115
- STDOUT.flush
116
- STDOUT.puts(with_message)
117
- end
118
- end
119
-
120
- spinner = Spinner.new
121
- pipe = Tracksperanto::Pipeline::Base.new(:progress_block => spinner.method(:spin).to_proc)
109
+ pbar = ProgressBar.new("Converting", 100)
110
+ pipe = Tracksperanto::Pipeline::Base.new(:progress_block => lambda{|p,m| pbar.set(p) }, :middleware_tuples => middlewares)
122
111
 
123
112
  if sole_format
124
113
  begin
@@ -129,18 +118,6 @@ if sole_format
129
118
  end
130
119
  end
131
120
 
132
- pipe.run(input_file, :pix_w => width, :pix_h => height, :parser => reader_klass) do | scaler, slipper, golden, reformat, shift, prefix, lerpm, len |
133
- slipper.slip = slip
134
- scaler.x_factor = scale_x
135
- scaler.y_factor = scale_y
136
- golden.enabled = golden_tracks
137
- reformat.width = reformat_w if reformat_w
138
- reformat.height = reformat_h if reformat_h
139
- shift.x_shift = x_shift
140
- shift.y_shift = y_shift
141
- prefix.prefix = set_prefix
142
- lerpm.enabled = lerp
143
- len.min_length = length_gate
144
- end
145
-
146
- spinner.finish("Converted #{pipe.converted_points} trackers with #{pipe.converted_keyframes} keys")
121
+ pipe.run(input_file, :width => width, :height => height, :parser => reader_klass)
122
+ pbar.finish
123
+ puts ("Converted #{pipe.converted_points} trackers with #{pipe.converted_keyframes} keys")
@@ -54,19 +54,10 @@ class Tracksperanto::Export::FlameStabilizer < Tracksperanto::Export::Base
54
54
  def export_point(frame, abs_float_x, abs_float_y, float_residual)
55
55
  flame_frame = frame + 1
56
56
  if @write_first_frame
57
- @base_x, @base_y = abs_float_x, abs_float_y
58
- write_first_frame(abs_float_x, abs_float_y)
59
- # For Flame to recognize the reference frame of the Shift channel
60
- # we need it to contain zero as an int, not as a float. The shift proceeds
61
- # from there.
62
- @x_shift_values = [[flame_frame, 0]]
63
- @y_shift_values = [[flame_frame, 0]]
57
+ export_first_point(flame_frame, abs_float_x, abs_float_y)
64
58
  @write_first_frame = false
65
59
  else
66
- # Just continue buffering the upcoming shift keyframes and flush them in the end
67
- shift_x, shift_y = @base_x - abs_float_x, @base_y - abs_float_y
68
- @x_shift_values.push([flame_frame, shift_x])
69
- @y_shift_values.push([flame_frame, shift_y])
60
+ export_remaining_point(flame_frame, abs_float_x, abs_float_y)
70
61
  end
71
62
  end
72
63
 
@@ -79,6 +70,23 @@ class Tracksperanto::Export::FlameStabilizer < Tracksperanto::Export::Base
79
70
 
80
71
  private
81
72
 
73
+ def export_remaining_point(flame_frame, abs_float_x, abs_float_y)
74
+ # Just continue buffering the upcoming shift keyframes and flush them in the end
75
+ shift_x, shift_y = @base_x - abs_float_x, @base_y - abs_float_y
76
+ @x_shift_values.push([flame_frame, shift_x])
77
+ @y_shift_values.push([flame_frame, shift_y])
78
+ end
79
+
80
+ def export_first_point(flame_frame, abs_float_x, abs_float_y)
81
+ @base_x, @base_y = abs_float_x, abs_float_y
82
+ write_first_frame(abs_float_x, abs_float_y)
83
+ # For Flame to recognize the reference frame of the Shift channel
84
+ # we need it to contain zero as an int, not as a float. The shift proceeds
85
+ # from there.
86
+ @x_shift_values = [[flame_frame, 0]]
87
+ @y_shift_values = [[flame_frame, 0]]
88
+ end
89
+
82
90
  # The shift channel is what determines how the tracking point moves.
83
91
  def write_shift_channel(name_without_prefix, values)
84
92
  @writer.channel(prefix(name_without_prefix)) do | c |
@@ -2,6 +2,7 @@
2
2
  class Tracksperanto::Export::ShakeText < Tracksperanto::Export::Base
3
3
  PREAMBLE = "TrackName %s\n Frame X Y Correlation\n"
4
4
  POSTAMBLE = "\n"
5
+ TEMPLATE = " %.2f %.3f %.3f %.3f"
5
6
 
6
7
  def self.desc_and_extension
7
8
  "shake_trackers.txt"
@@ -21,7 +22,7 @@ class Tracksperanto::Export::ShakeText < Tracksperanto::Export::Base
21
22
 
22
23
  def export_point(frame, abs_float_x, abs_float_y, float_residual)
23
24
  # Shake starts from frame 1, not 0
24
- line = " %.2f %.3f %.3f %.3f" % [frame + 1, abs_float_x, abs_float_y, 1 - float_residual]
25
+ line = TEMPLATE % [frame + 1, abs_float_x, abs_float_y, 1 - float_residual]
25
26
  @io.puts line
26
27
  end
27
28
  end
@@ -14,9 +14,6 @@ class Tracksperanto::Import::FlameStabilizer < Tracksperanto::Import::Base
14
14
  "Flame .stabilizer file"
15
15
  end
16
16
 
17
- T = ::Tracksperanto::Tracker
18
- K = ::Tracksperanto::Keyframe
19
-
20
17
  class ChannelBlock < Array
21
18
  include ::Tracksperanto::Casts
22
19
  cast_to_string :name
@@ -175,7 +172,7 @@ Channel tracker1/ref/x
175
172
  end
176
173
 
177
174
  def grab_tracker(channels, track_x)
178
- t = T.new(:name => track_x.name.split('/').shift)
175
+ t = Tracksperanto::Tracker.new(:name => track_x.name.split('/').shift)
179
176
 
180
177
  report_progress("Extracting tracker #{t.name}")
181
178
 
@@ -200,7 +197,7 @@ Channel tracker1/ref/x
200
197
  kf_x, kf_y = base_x - x.to_f, base_y - y.to_f
201
198
 
202
199
  report_progress("Extracting keyframe #{total_kf += 1} of #{t.name}")
203
- K.new(:frame => (at - 1), :abs_x => kf_x, :abs_y => kf_y)
200
+ Tracksperanto::Keyframe.new(:frame => (at - 1), :abs_x => kf_x, :abs_y => kf_y)
204
201
  end
205
202
 
206
203
  return t
@@ -7,8 +7,7 @@ class Tracksperanto::Import::ShakeText < Tracksperanto::Import::Base
7
7
 
8
8
  def parse(io)
9
9
  trackers = []
10
- until io.eof?
11
- line = io.gets
10
+ io.each do | line |
12
11
  if line =~ /TrackName (.+)/
13
12
  trackers << Tracksperanto::Tracker.new{|t| t.name = $1 }
14
13
  # Toss the next following string - header
data/lib/pipeline/base.rb CHANGED
@@ -4,7 +4,7 @@
4
4
  # pipe = Tracksperanto::Pipeline::Base.new
5
5
  # pipe.progress_block = lambda{|percent, msg| puts("#{msg}..#{percent.to_i}%") }
6
6
  #
7
- # pipe.run("/tmp/shakescript.shk", :pix_w => 720, :pix_h => 576) do | *all_middlewares |
7
+ # pipe.run("/tmp/shakescript.shk", :width => 720, :height => 576) do | *all_middlewares |
8
8
  # # configure middlewares here
9
9
  # end
10
10
  #
@@ -12,6 +12,7 @@
12
12
  # at the same place where the original file resides,
13
13
  # and setup outputs for all supported export formats.
14
14
  class Tracksperanto::Pipeline::Base
15
+ EXTENSION = /\.([^\.]+)$/ #:nodoc:
15
16
 
16
17
  include Tracksperanto::BlockInit
17
18
 
@@ -31,69 +32,77 @@ class Tracksperanto::Pipeline::Base
31
32
  # Assign an array of exporters to use them instead of the standard ones
32
33
  attr_accessor :exporters
33
34
 
34
- DEFAULT_OPTIONS = {:pix_w => 720, :pix_h => 576, :parser => Tracksperanto::Import::ShakeScript }
35
+ DEFAULT_OPTIONS = {:width => 720, :height => 576, :parser => Tracksperanto::Import::ShakeScript }
36
+
37
+ # Contains arrays of the form ["MiddewareName", {:param => value}]
38
+ attr_accessor :middleware_tuples
39
+
40
+ def wrap_output_with_middlewares(output)
41
+ return output unless (middleware_tuples && middleware_tuples.any?)
42
+
43
+ middleware_tuples.reverse.inject(output) do | wrapped, (middleware_name, options) |
44
+ Tracksperanto.get_middleware(middleware_name).new(wrapped, options || {})
45
+ end
46
+ end
35
47
 
36
48
  # Runs the whole pipeline. Accepts the following options
37
- # * pix_w - The comp width, for the case that the format does not support auto size
38
- # * pix_h - The comp height, for the case that the format does not support auto size
49
+ # * width - The comp width, for the case that the format does not support auto size
50
+ # * height - The comp height, for the case that the format does not support auto size
39
51
  # * parser - The parser class, for the case that it can't be autodetected from the file name
40
52
  def run(from_input_file_path, passed_options = {}) #:yields: *all_middlewares
41
53
  o = DEFAULT_OPTIONS.merge(passed_options)
42
- pix_w, pix_h, parser_class = detect_importer_or_use_options(from_input_file_path, o)
43
54
 
44
55
  # Reset stats
45
56
  @converted_keyframes, @converted_points = 0, 0
46
57
 
47
- # Grab the input
48
- read_data = File.open(from_input_file_path, "rb")
49
-
50
58
  # Assign the parser
51
- importer = parser_class.new(:width => pix_w, :height => pix_h)
59
+ importer = initialize_importer_with_path_and_options(from_input_file_path, o)
52
60
 
61
+ # Open the file
62
+ read_data = File.open(from_input_file_path, "rb")
63
+
53
64
  # Setup a multiplexer
54
65
  mux = setup_outputs_for(from_input_file_path)
55
66
 
56
67
  # Setup middlewares
57
- middlewares = setup_middleware_chain_with(mux)
58
-
59
- # Yield middlewares to the block
60
- yield(*middlewares) if block_given?
61
-
62
- @converted_points, @converted_keyframes = run_export(read_data, importer, middlewares[-1])
68
+ endpoint = wrap_output_with_middlewares(mux)
69
+ @converted_points, @converted_keyframes = run_export(read_data, importer, endpoint)
63
70
  end
64
71
 
65
72
  def report_progress(percent_complete, message)
66
73
  @progress_block.call(percent_complete, message) if @progress_block
67
74
  end
68
75
 
69
- def detect_importer_or_use_options(path, opts)
70
- d = Tracksperanto::FormatDetector.new(path)
76
+ def initialize_importer_with_path_and_options(from_input_file_path, options)
77
+ d = Tracksperanto::FormatDetector.new(from_input_file_path)
71
78
  if d.match? && d.auto_size?
72
- return [1, 1, d.importer_klass]
79
+ d.importer_klass.new
73
80
  elsif d.match?
74
- raise "Width and height must be provided for a #{d.importer_klass}" unless (opts[:pix_w] && opts[:pix_h])
75
- opts[:parser] = d.importer_klass
81
+ require_dimensions_in!(opts)
82
+ d.importer_klass.new(:width => opts[:width], :height => opts[:height])
76
83
  else
77
84
  raise "Cannot autodetect the file format - please specify the importer explicitly" unless opts[:parser]
78
- unless opts[:parser].autodetects_size?
79
- raise "Width and height must be provided for this importer" unless (opts[:pix_w] && opts[:pix_h])
80
- end
85
+ klass = Tracksperanto.get_exporter(opts[:parser])
86
+ require_dimensions_in!(opts) unless klass.autodetects_size?
87
+ klass.new(:width => opts[:width], :height => opts[:height])
81
88
  end
82
-
83
- [opts[:pix_w], opts[:pix_h], opts[:parser]]
89
+ end
90
+
91
+ def require_dimensions_in!(opts)
92
+ raise "Width and height must be provided for this importer" unless (opts[:width] && opts[:height])
84
93
  end
85
94
 
86
95
  # Runs the export and returns the number of points and keyframes processed.
87
96
  # If a block is passed, the block will receive the percent complete and the last
88
97
  # status message that you can pass back to the UI
89
- def run_export(tracker_data_io, parser, processor)
98
+ def run_export(tracker_data_io, parser, exporter)
90
99
  points, keyframes, percent_complete = 0, 0, 0.0
91
-
100
+
92
101
  report_progress(percent_complete, "Starting the parser")
93
-
102
+
94
103
  # Report progress from the parser
95
104
  parser.progress_block = lambda { | m | report_progress(percent_complete, m) }
96
-
105
+
97
106
  # Wrap the input in a progressive IO, setup a lambda that will spy on the reader and
98
107
  # update the percentage. We will only broadcast messages that come from the parser
99
108
  # though (complementing it with a percentage)
@@ -101,34 +110,34 @@ class Tracksperanto::Pipeline::Base
101
110
  percent_complete = (50.0 / of_total) * offset
102
111
  end
103
112
  @ios << io_with_progress
104
-
113
+
105
114
  trackers = parser.parse(io_with_progress)
106
-
115
+
107
116
  report_progress(percent_complete = 50.0, "Validating #{trackers.length} imported trackers")
108
-
117
+
109
118
  validate_trackers!(trackers)
110
-
119
+
111
120
  report_progress(percent_complete, "Starting export")
112
-
121
+
113
122
  percent_per_tracker = (100.0 - percent_complete) / trackers.length
114
-
123
+
115
124
  # Use the width and height provided by the parser itself
116
- processor.start_export(parser.width, parser.height)
125
+ exporter.start_export(parser.width, parser.height)
117
126
  trackers.each_with_index do | t, tracker_idx |
118
127
  kf_weight = percent_per_tracker / t.keyframes.length
119
128
  points += 1
120
- processor.start_tracker_segment(t.name)
129
+ exporter.start_tracker_segment(t.name)
121
130
  t.each_with_index do | kf, idx |
122
131
  keyframes += 1
123
- processor.export_point(kf.frame, kf.abs_x, kf.abs_y, kf.residual)
132
+ exporter.export_point(kf.frame, kf.abs_x, kf.abs_y, kf.residual)
124
133
  report_progress(percent_complete += kf_weight, "Writing keyframe #{idx+1} of #{t.name.inspect}, #{trackers.length - tracker_idx} trackers to go")
125
134
  end
126
- processor.end_tracker_segment
135
+ exporter.end_tracker_segment
127
136
  end
128
- processor.end_export
129
-
137
+ exporter.end_export
138
+
130
139
  report_progress(100.0, "Wrote #{points} points and #{keyframes} keyframes")
131
-
140
+
132
141
  [points, keyframes]
133
142
  ensure
134
143
  @ios.reject!{|e| e.close unless (!e.respond_to?(:closed?) || e.closed?) } if @ios
@@ -137,7 +146,7 @@ class Tracksperanto::Pipeline::Base
137
146
  # Setup output files and return a single output
138
147
  # that replays to all of them
139
148
  def setup_outputs_for(input_file_path)
140
- file_name = File.basename(input_file_path).gsub(/\.([^\.]+)$/, '')
149
+ file_name = File.basename(input_file_path).gsub(EXTENSION, '')
141
150
  export_klasses = exporters || Tracksperanto.exporters
142
151
  Tracksperanto::Export::Mux.new(
143
152
  export_klasses.map do | exporter_class |
@@ -148,21 +157,6 @@ class Tracksperanto::Pipeline::Base
148
157
  )
149
158
  end
150
159
 
151
- # Setup and return the output multiplexor wrapped in all necessary middlewares.
152
- # Middlewares must be returned in an array to be passed to the configuration block
153
- # afterwards
154
- def setup_middleware_chain_with(output)
155
- scaler = Tracksperanto::Middleware::Scaler.new(output)
156
- slipper = Tracksperanto::Middleware::Slipper.new(scaler)
157
- golden = Tracksperanto::Middleware::Golden.new(slipper)
158
- reformat = Tracksperanto::Middleware::Reformat.new(golden)
159
- shift = Tracksperanto::Middleware::Shift.new(reformat)
160
- prefix = Tracksperanto::Middleware::Prefix.new(shift)
161
- lerp = Tracksperanto::Middleware::Lerp.new(prefix)
162
- lengate = Tracksperanto::Middleware::LengthCutoff.new(lerp)
163
- [scaler, slipper, golden, reformat, shift, prefix, lerp, lengate]
164
- end
165
-
166
160
  # Open the file for writing and register it to be closed automatically
167
161
  def open_owned_export_file(path_to_file)
168
162
  @ios ||= []
data/lib/tracksperanto.rb CHANGED
@@ -4,7 +4,7 @@ require 'tempfile'
4
4
 
5
5
  module Tracksperanto
6
6
  PATH = File.expand_path(File.dirname(__FILE__))
7
- VERSION = '1.8.4'
7
+ VERSION = '1.9.0'
8
8
 
9
9
  module Import; end
10
10
  module Export; end
@@ -38,7 +38,16 @@ module Tracksperanto
38
38
  end
39
39
 
40
40
  self.exporters, self.importers, self.middlewares = [], [], []
41
-
41
+
42
+ # Case-insensitive search for a middleware class by name
43
+ def self.get_middleware(name)
44
+ middlewares.each do | x |
45
+ return x if x.const_name.downcase == name.downcase
46
+ end
47
+
48
+ raise NameError, "Unknown middleware #{name}"
49
+ end
50
+
42
51
  # Case-insensitive search for an export module by name
43
52
  def self.get_exporter(name)
44
53
  exporters.each do | x |
@@ -5,7 +5,7 @@ require "tempfile"
5
5
  class Tracksperanto::BufferIO < DelegateClass(IO)
6
6
  include Tracksperanto::Returning
7
7
 
8
- IN_MEMORY_SIZE_LIMIT = 100_000
8
+ MAX_IN_MEM_BYTES = 100_000
9
9
 
10
10
  def initialize
11
11
  __setobj__(StringIO.new)
@@ -37,12 +37,11 @@ class Tracksperanto::BufferIO < DelegateClass(IO)
37
37
  def replace_with_tempfile_if_needed
38
38
  return if @tempfile_in
39
39
  io = __getobj__
40
- if io.pos > IN_MEMORY_SIZE_LIMIT
41
- @tempfile_in = true
40
+ if io.pos > MAX_IN_MEM_BYTES
42
41
  tf = Tempfile.new("tracksperanto-xbuf")
43
- io.rewind
44
- tf.write(io.read) until io.eof?
42
+ tf.write(io.string)
45
43
  __setobj__(tf)
44
+ @tempfile_in = true
46
45
  end
47
46
  end
48
47
  end
@@ -12,9 +12,7 @@ class BoujouImportTest < Test::Unit::TestCase
12
12
  def test_parsing_from_fixture
13
13
  fixture = File.open(File.dirname(__FILE__) + '/samples/boujou_features_text/boujou_txt_export.txt')
14
14
 
15
- parser = Tracksperanto::Import::Boujou.new
16
- parser.width = 2560
17
- parser.height = 1200
15
+ parser = Tracksperanto::Import::Boujou.new(:width => 2560, :height => 1200)
18
16
 
19
17
  trackers = parser.parse(fixture)
20
18
  assert_equal 20, trackers.length
@@ -9,7 +9,7 @@ class PFTrackImportTest < Test::Unit::TestCase
9
9
  assert !i.autodetects_size?
10
10
  end
11
11
 
12
- def test_parsing_from_pftrack5_second_file
12
+ def test_parsing_from_pftrack5_with_digit_names
13
13
  fixture = File.open(File.dirname(__FILE__) + '/samples/pftrack5/apft.2dt')
14
14
  parser = Tracksperanto::Import::PFTrack.new(:width => 1920, :height => 1080)
15
15
 
@@ -42,6 +42,11 @@ class TracksperantoTest < Test::Unit::TestCase
42
42
  assert_equal i1, Tracksperanto::Import::Syntheyes
43
43
  end
44
44
 
45
+ def test_get_middleware
46
+ i1 = Tracksperanto.get_middleware("lERP")
47
+ assert_equal i1, Tracksperanto::Middleware::Lerp
48
+ end
49
+
45
50
  def test_get_importer_multicase
46
51
  i1 = Tracksperanto.get_importer("ShakeScript")
47
52
  assert_equal i1, Tracksperanto::Import::ShakeScript
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tracksperanto
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.4
4
+ version: 1.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Julik Tarkhanov
@@ -9,9 +9,19 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-03-10 00:00:00 +01:00
12
+ date: 2010-03-21 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: progressbar
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
15
25
  - !ruby/object:Gem::Dependency
16
26
  name: flexmock
17
27
  type: :development