tracksperanto 1.8.4 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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