tracksperanto 2.9.5 → 2.9.6

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.
Files changed (51) hide show
  1. data/Gemfile +1 -1
  2. data/History.txt +5 -0
  3. data/bin/tracksperanto +32 -22
  4. data/lib/export/equalizer3.rb +1 -1
  5. data/lib/export/equalizer4.rb +1 -1
  6. data/lib/import/flame_stabilizer.rb +3 -3
  7. data/lib/import/nuke_script.rb +25 -7
  8. data/lib/middleware/base.rb +14 -7
  9. data/lib/middleware/crop.rb +9 -2
  10. data/lib/middleware/flip.rb +5 -3
  11. data/lib/middleware/flop.rb +4 -2
  12. data/lib/middleware/golden.rb +5 -2
  13. data/lib/middleware/length_cutoff.rb +6 -2
  14. data/lib/middleware/lerp.rb +5 -2
  15. data/lib/middleware/lint.rb +5 -0
  16. data/lib/middleware/move_to_first.rb +50 -0
  17. data/lib/middleware/pad.rb +8 -2
  18. data/lib/middleware/prefix.rb +6 -2
  19. data/lib/middleware/reformat.rb +6 -3
  20. data/lib/middleware/scaler.rb +6 -2
  21. data/lib/middleware/shift.rb +7 -2
  22. data/lib/middleware/slipper.rb +6 -2
  23. data/lib/middleware/start_trim.rb +6 -6
  24. data/lib/pipeline/base.rb +34 -38
  25. data/lib/tracksperanto.rb +18 -1
  26. data/lib/tracksperanto/format_detector.rb +26 -26
  27. data/lib/tracksperanto/parameters.rb +56 -0
  28. data/lib/tracksperanto/yield_non_empty.rb +15 -0
  29. data/test/import/test_nuke_import.rb +16 -1
  30. data/test/middleware/test_crop_middleware.rb +5 -0
  31. data/test/middleware/test_flip_middleware.rb +4 -19
  32. data/test/middleware/test_flop_middleware.rb +4 -9
  33. data/test/middleware/test_golden_middleware.rb +3 -10
  34. data/test/middleware/test_length_cutoff_middleware.rb +5 -0
  35. data/test/middleware/test_lerp_middleware.rb +6 -20
  36. data/test/middleware/test_lint_middleware.rb +5 -0
  37. data/test/middleware/test_move_to_first_frame_middleware.rb +38 -0
  38. data/test/middleware/test_pad_middleware.rb +5 -0
  39. data/test/middleware/test_prefix.rb +4 -0
  40. data/test/middleware/test_reformat_middleware.rb +3 -2
  41. data/test/middleware/test_scaler_middleware.rb +5 -0
  42. data/test/middleware/test_shift_middleware.rb +5 -0
  43. data/test/middleware/test_slip_middleware.rb +5 -0
  44. data/test/middleware/test_start_trim_middleware.rb +3 -9
  45. data/test/test_cli.rb +3 -3
  46. data/test/test_format_detector.rb +1 -1
  47. data/test/test_parameters.rb +38 -0
  48. data/test/test_tracksperanto.rb +10 -0
  49. data/test/test_yield_non_empty.rb +11 -0
  50. data/tracksperanto.gemspec +11 -5
  51. metadata +27 -21
data/Gemfile CHANGED
@@ -1,7 +1,7 @@
1
1
  # -*- ruby -*-
2
2
  source :rubygems
3
3
 
4
- gem "obuf", "~> 1.0.4"
4
+ gem "obuf", "~> 1.1.0"
5
5
  gem "progressive_io", "~> 1.0"
6
6
  gem "flame_channel_parser", "~> 4.0"
7
7
  gem "progressbar", "0.10.0"
data/History.txt CHANGED
@@ -1,3 +1,8 @@
1
+ === 2.9.6
2
+
3
+ * Add Nuke Reconcile3D support
4
+ * Add Middleware::Base.parameters for automatic GUI building
5
+
1
6
  === 2.9.5
2
7
 
3
8
  * Export a Maya ASCII scene instead of a MEL script
data/bin/tracksperanto CHANGED
@@ -17,7 +17,7 @@ require 'progressbar'
17
17
  require "update_hints"
18
18
 
19
19
  def disclaimer
20
- "Please consider a small donation to keep Tracksperanto going. http://guerilla-di.org/source-and-license/\n"+
20
+ "Please consider a small donation to keep Tracksperanto going: http://guerilla-di.org/source-and-license/\n"+
21
21
  "For information and support please contact info#{64.chr}guerilla-di.org"
22
22
  end
23
23
 
@@ -29,13 +29,21 @@ writers = Tracksperanto.exporter_names
29
29
 
30
30
  op = OptionParser.new
31
31
 
32
- def mw(name, option, default_value = nil)
32
+ def mw(*name_option_and_default_value)
33
33
  Proc.new do |value|
34
+ name, option, default_value = name_option_and_default_value
34
35
  v = value.nil? ? default_value : value
35
- $middlewares.push([name, {option => v}])
36
+ mw_tuple = [name]
37
+ mw_tuple.push({option => v}) if option
38
+
39
+ $middlewares.push(mw_tuple)
36
40
  end
37
41
  end
38
42
 
43
+ def mwd(name)
44
+ Tracksperanto.get_middleware(name).action_description
45
+ end
46
+
39
47
  def list_exporters(dest = $stderr)
40
48
  dest.puts "The following export modules are available:"
41
49
  Tracksperanto.exporters.each do | exporter |
@@ -66,32 +74,35 @@ op.on(" -w", "--width WIDTH_IN_PIXELS", Integer, "Absolute input comp width in p
66
74
  op.on(" -h", "--height HEIGHT_IN_PIXELS", Integer, "Absolute input comp height in pixels (will try to autodetect)") {|w| options[:height] = w }
67
75
  op.on(" -o", "--only EXPORTER_NAME", String, "Only export the selected format, format must be one of #{writers.join(", ")}") { |f| writer_class_name = f }
68
76
 
69
- op.on(" -xs", "--xscale X_SCALING_FACTOR", Float, "Scale the result in X by this factor (1.0 is the default)", &mw("Scaler", :x_factor))
77
+ op.on(" -xs", "--xscale X_SCALING_FACTOR", Float, mwd("Scaler"), &mw("Scaler", :x_factor))
70
78
 
71
- op.on(" -pad", "--pad PAD_FRACTION_VALUES_COMMA_SEPARATED", String, "Pad the comp by these ratios") do | pads|
79
+ op.on(" -pad", "--pad PAD_FRACTION_VALUES_COMMA_SEPARATED", String, mwd("Pad")) do | pads|
72
80
  left, right, top, bottom = pads.split(",").map{|e| e.to_f }
73
81
  $middlewares.push(["Pad", {"left_pad" => left, "right_pad"=> right, "top_pad" => top, "bottom_pad" => bottom}])
74
82
  end
75
83
 
76
- op.on(" -crop", "--crop CROP_VALUES_COMMA_SEPARATED", String, "Crop or unpad the comp by these pixel values") do | pads|
84
+ op.on(" -crop", "--crop CROP_VALUES_COMMA_SEPARATED", String, mwd("Crop")) do | pads|
77
85
  left, right, top, bottom = pads.split(",").map{|e| e.to_i }
78
86
  $middlewares.push(["Crop", {"left" => left, "right"=> right, "top" => top, "bottom" => bottom}])
79
87
  end
80
88
 
81
- op.on(" -ys", "--yscale Y_SCALING_FACTOR", Float, "Scale the result in Y by this factor (1.0 is the default)", &mw("Scaler", :y_factor))
82
- op.on(" -t", "--trim", "Trim the start frames (do not allow negative frame values)", &mw("StartTrim", :enabled, true)) # Before slip!
83
- op.on(" -s", "--slip FRAMES", Integer, "Slip the result by this number of frames, positive is 'later'", &mw("Slipper", :slip))
84
-
85
- op.on(" -g", "--golden", "Reset the residuals of all trackers to 0 (ignore correlation)", &mw("Golden", :enabled, true))
89
+ op.on(" -ys", "--yscale Y_SCALING_FACTOR", Float, mwd("Scaler"), &mw("Scaler", :y_factor))
90
+ op.on(" -t", "--trim", Float, mwd("StartTrim"), &mw("StartTrim")) # Before slip!
91
+ op.on(" -s", "--slip FRAMES", Integer, mwd("Slipper"), &mw("Slipper", :slip))
92
+ op.on(" -g", "--golden", mwd("Golden"), &mw("Golden"))
93
+ op.on(" -m", "--min-length LENGTH_IN_FRAMES", Integer, mwd("LengthCutoff"), &mw("LengthCutoff", :min_length))
94
+ op.on(" -p", "--prefix PREFIX", String, mwd("Prefix"), &mw("Prefix", :prefix))
95
+ op.on("--lerp", mwd("Lerp"), &mw("Lerp"))
96
+ op.on("--flip", mwd("Flip"), &mw("Flip"))
97
+ op.on("--flop", mwd("Flop"), &mw("Flop"))
98
+
99
+ # TODO - multiparameters
86
100
  op.on(" -rx", "--reformat-x NEW_PIX_WIDTH", Integer, "Reformat the comp to this width and scale all tracks to it", &mw("Reformat", :width))
87
101
  op.on(" -ry", "--reformat-y NEW_PIX_HEIGHT", Integer, "Reformat the comp to this height and scale all tracks to it", &mw("Reformat", :height))
88
- op.on(" -m", "--min-length LENGTH_IN_FRAMES", Integer, "Only export trackers having more than X keyframes", &mw("LengthCutoff", :min_length))
89
102
  op.on(" -xm", "--xshift X_IN_PIXELS", Float, "Move the points left or right", &mw("Shift", :x_shift))
90
103
  op.on(" -ym", "--yshift Y_IN_PIXELS", Float, "Move the points up or down", &mw("Shift", :y_shift))
91
- op.on(" -p", "--prefix PREFIX", String, "A prefix to prepend to tracker names in bulk", &mw("Prefix", :prefix))
92
- op.on("--lerp", "Linearly interpolate missing keyframes", &mw("Lerp", :enabled, true))
93
- op.on("--flip", "Flip the comp horizontally", &mw("Flip", :enabled, true))
94
- op.on("--flop", "Flop the comp vertically", &mw("Flop", :enabled, true))
104
+
105
+
95
106
  op.on("--list-exporters", "Show available export modules") do
96
107
  list_exporters; exit(0)
97
108
  end
@@ -131,22 +142,21 @@ begin
131
142
  pipe = Tracksperanto::Pipeline::Base.new(:progress_block => progress, :middleware_tuples => $middlewares)
132
143
  pipe.exporters = [Tracksperanto.get_exporter(writer_class_name)] if writer_class_name
133
144
  pipe.run(input_file, options)
145
+ pbar.finish
146
+ $stdout.puts "Found and converted %d trackers with %d keyframes." % [pipe.converted_points, pipe.converted_keyframes]
134
147
  rescue Tracksperanto::UnknownExporterError => damn
135
148
  $stderr.puts damn.message
136
149
  list_exporters($stderr)
137
- exit(2)
150
+ fail "Unknown exporter"
138
151
  rescue Tracksperanto::UnknownImporterError => damn
139
152
  $stderr.puts damn.message
140
153
  list_importers($stderr)
141
- exit(2)
154
+ fail "Unknown importer"
142
155
  rescue Tracksperanto::UnknownMiddlewareError => damn
143
156
  $stderr.puts damn.message
144
- $stderr.puts "This is a bug, please report it"
145
- exit(2)
157
+ fail "This is a bug, please report it"
146
158
  rescue Exception => damn
147
159
  fail damn.message
148
- ensure
149
- pbar.finish
150
160
  end
151
161
 
152
162
  puts disclaimer
@@ -1,5 +1,5 @@
1
1
  # -*- encoding : utf-8 -*-
2
- # Export for 3DE v3 point files
2
+ # Export for 3DE v3 point files. 3DE always starts frames at 1.
3
3
  class Tracksperanto::Export::Equalizer3 < Tracksperanto::Export::Base
4
4
 
5
5
  HEADER = '// 3DE Multiple Tracking Curves Export %d x %d * %d frames'
@@ -1,5 +1,5 @@
1
1
  # -*- encoding : utf-8 -*-
2
- # Export for 3DE v4 point files
2
+ # Export for 3DE v4 point files. 3DE always starts frames at 1.
3
3
  class Tracksperanto::Export::Equalizer4 < Tracksperanto::Export::Base
4
4
 
5
5
  def self.desc_and_extension
@@ -131,8 +131,8 @@ class Tracksperanto::Import::FlameStabilizer < Tracksperanto::Import::Base
131
131
 
132
132
  def find_base_x_and_y(track_tuples, shift_tuples)
133
133
  base_track_tuple = track_tuples.find do | track_tuple |
134
- shift_tuples.find { |shift_tuple| shift_tuple[0] == track_tuple [0] }
135
- end || track_tuples[0]
136
- base_track_tuple[1..2]
134
+ shift_tuples.find { |shift_tuple| shift_tuple[0] == track_tuple[0] }
135
+ end
136
+ (base_track_tuple || track_tuples[0])[1..2]
137
137
  end
138
138
  end
@@ -5,7 +5,7 @@ require File.expand_path(File.dirname(__FILE__)) + "/nuke_grammar/utils"
5
5
  class Tracksperanto::Import::NukeScript < Tracksperanto::Import::Base
6
6
 
7
7
  def self.human_name
8
- "Nuke .nk script file"
8
+ "Nuke .nk script file with Tracker or Reconcile3D nodes"
9
9
  end
10
10
 
11
11
  def self.distinct_file_ext
@@ -16,7 +16,9 @@ class Tracksperanto::Import::NukeScript < Tracksperanto::Import::Base
16
16
  io = Tracksperanto::ExtIO.new(@io)
17
17
  while line = io.gets_and_strip
18
18
  if line =~ TRACKER_3_PATTERN
19
- scan_tracker_node(io).each { |t| yield(t) }
19
+ scan_tracker_node(io).each(&Proc.new)
20
+ elsif line =~ RECONCILE_PATTERN
21
+ scan_reconcile_node(io).each(&Proc.new)
20
22
  end
21
23
  end
22
24
  end
@@ -24,9 +26,26 @@ class Tracksperanto::Import::NukeScript < Tracksperanto::Import::Base
24
26
  private
25
27
 
26
28
  TRACKER_3_PATTERN = /^Tracker3 \{/
29
+ RECONCILE_PATTERN = /^Reconcile3D \{/
30
+ OUTPUT_PATTERN = /^output \{/
27
31
  TRACK_PATTERN = /^track(\d) \{/
28
32
  NODENAME = /^name ([^\n]+)/
29
33
 
34
+
35
+ # Scans a Reconcile3D node and returs it's output
36
+ def scan_reconcile_node(io)
37
+ t = Tracksperanto::Tracker.new
38
+ while line = io.gets_and_strip
39
+ if line =~ OUTPUT_PATTERN
40
+ t = extract_tracker(line)
41
+ elsif line =~ NODENAME
42
+ t.name = "Reconcile_#{$1}"
43
+ report_progress("Scavenging Reconcile3D node #{t.name}")
44
+ return [t] # Klunky
45
+ end
46
+ end
47
+ end
48
+
30
49
  # Scans a tracker node and return all tracks within that node (no more than 4)
31
50
  def scan_tracker_node(io)
32
51
  tracks_in_tracker = []
@@ -56,11 +75,10 @@ class Tracksperanto::Import::NukeScript < Tracksperanto::Import::Base
56
75
  def extract_tracker(line)
57
76
  tuples = scan_track(line)
58
77
  return nil unless (tuples && tuples.any?)
59
-
60
- Tracksperanto::Tracker.new(
61
- :keyframes => tuples.map do | (f, x, y) |
62
- Tracksperanto::Keyframe.new(:frame => f -1, :abs_x => x, :abs_y => y)
78
+ Tracksperanto::Tracker.new do | t |
79
+ tuples.each do | (f, x, y) |
80
+ t.keyframe!(:frame => (f -1), :abs_x => x, :abs_y => y)
63
81
  end
64
- )
82
+ end
65
83
  end
66
84
  end
@@ -1,4 +1,6 @@
1
1
  # -*- encoding : utf-8 -*-
2
+ require "forwardable"
3
+
2
4
  # The base middleware class works just like a Tracksperanto::Export::Base, but it only wraps another exporting object and does not get registered on it's own
3
5
  # as an export format. Middleware can be used to massage the tracks being exported in various interesting ways - like moving the coordinates, clipping the keyframes,
4
6
  # scaling the whole export or even reversing the trackers to go backwards
@@ -7,6 +9,10 @@ class Tracksperanto::Middleware::Base
7
9
  include Tracksperanto::BlockInit
8
10
  include Tracksperanto::ConstName
9
11
  include Tracksperanto::SimpleExport
12
+ include Tracksperanto::Parameters
13
+
14
+ extend Forwardable
15
+ def_delegators :@exporter, :start_export, :start_tracker_segment, :end_tracker_segment, :export_point, :end_export
10
16
 
11
17
  # Used to automatically register your middleware in Tracksperanto.middlewares
12
18
  # Normally you wouldn't need to override this
@@ -15,15 +21,16 @@ class Tracksperanto::Middleware::Base
15
21
  super
16
22
  end
17
23
 
18
- # Constructor accepts the exporter that will be wrapped
24
+ # Returns the human name of the action that the middleware will perform. The action is
25
+ # the in infinitive form, like "Remove all the invalid keyframes", "Crop the image" and so on
26
+ def self.action_description
27
+ "Base middleware class"
28
+ end
29
+
30
+ # Constructor accepts the exporter that will be wrapped, followed by the optional options hash
31
+ # and the optional block that yields self
19
32
  def initialize(*exporter_and_args_for_block_init)
20
33
  @exporter = exporter_and_args_for_block_init.shift
21
34
  super
22
35
  end
23
-
24
- %w( start_export start_tracker_segment end_tracker_segment
25
- export_point end_export).each do | m |
26
- define_method(m){|*a| @exporter.send(m, *a)}
27
- end
28
-
29
36
  end
@@ -1,8 +1,15 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  # Does the same as the Pad middleware but with absolute pixel values instead of fractionals
3
3
  class Tracksperanto::Middleware::Crop < Tracksperanto::Middleware::Base
4
- attr_accessor :top, :left, :right, :bottom
5
- cast_to_int :top, :left, :right, :bottom
4
+
5
+ parameter :top, :cast => :int, :desc => "Top crop amount in px"
6
+ parameter :left, :cast => :int, :desc => "Left crop amount in px"
7
+ parameter :right, :cast => :int, :desc => "Right crop amount in px"
8
+ parameter :bottom, :cast => :int, :desc => "Bottom crop amount in px"
9
+
10
+ def self.action_description
11
+ "Crop or pad the image by a specified number of pixels"
12
+ end
6
13
 
7
14
  def start_export(w, h)
8
15
  left_pad, right_pad, top_pad, bottom_pad = (left / w.to_f), (right / w.to_f), (top / h.to_f), (bottom / h.to_f)
@@ -1,11 +1,13 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  # Flips the comp being exported horizontally
3
3
  class Tracksperanto::Middleware::Flip < Tracksperanto::Middleware::Base
4
-
5
- attr_accessor :enabled
4
+
5
+ def self.action_description
6
+ "Mirrors all the tracker paths horizontally"
7
+ end
6
8
 
7
9
  def start_export(w, h)
8
- factor = enabled ? -1 : 1
10
+ factor = -1
9
11
  @exporter = Tracksperanto::Middleware::Scaler.new(@exporter, :x_factor => factor)
10
12
  super
11
13
  end
@@ -2,10 +2,12 @@
2
2
  # Flips the comp being exported vertically
3
3
  class Tracksperanto::Middleware::Flop < Tracksperanto::Middleware::Base
4
4
 
5
- attr_accessor :enabled
5
+ def self.action_description
6
+ "Mirror all the tracker paths vertically"
7
+ end
6
8
 
7
9
  def start_export(w, h)
8
- factor = enabled ? -1 : 1
10
+ factor = -1
9
11
  @exporter = Tracksperanto::Middleware::Scaler.new(@exporter, :y_factor => factor)
10
12
  super
11
13
  end
@@ -1,10 +1,13 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  # This middleware marks all trackers as being 100% accurate
3
3
  class Tracksperanto::Middleware::Golden < Tracksperanto::Middleware::Base
4
- attr_accessor :enabled
4
+
5
+ def self.action_description
6
+ "Reset residual of all the trackers to zero"
7
+ end
5
8
 
6
9
  def export_point(frame, float_x, float_y, float_residual)
7
- super(frame, float_x, float_y, (enabled ? 0.0 : float_residual))
10
+ super(frame, float_x, float_y, 0.0)
8
11
  end
9
12
 
10
13
  end
@@ -2,8 +2,12 @@
2
2
  # This middleware removes trackers that contain less than min_length keyframes
3
3
  # from the exported batch
4
4
  class Tracksperanto::Middleware::LengthCutoff < Tracksperanto::Middleware::Base
5
- attr_accessor :min_length
6
- cast_to_int :min_length
5
+
6
+ parameter :min_length, :cast => :int, :desc => "The minimum number of keyframes for the item to contain", :default => 100
7
+
8
+ def self.action_description
9
+ "Remove trackers that have less than the specified number of keyframes"
10
+ end
7
11
 
8
12
  def start_tracker_segment(name)
9
13
  @tracker = Tracksperanto::Tracker.new(:name => name)
@@ -1,7 +1,10 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  # This middleware adds linearly interpolated keyframes BETWEEN the keyframes passing through the exporter
3
3
  class Tracksperanto::Middleware::Lerp < Tracksperanto::Middleware::Base
4
- attr_accessor :enabled
4
+
5
+ def self.action_description
6
+ "Interpolate missing keyframes of all the trackers"
7
+ end
5
8
 
6
9
  def end_tracker_segment
7
10
  @last_f, @last_x, @last_y, @last_res = nil, nil, nil, nil
@@ -10,7 +13,7 @@ class Tracksperanto::Middleware::Lerp < Tracksperanto::Middleware::Base
10
13
 
11
14
  def export_point(frame, float_x, float_y, float_residual)
12
15
 
13
- if @enabled && @last_f && (frame - @last_f > 1) # Interpolate!
16
+ if @last_f && (frame - @last_f > 1) # Interpolate!
14
17
  interpolated_frames = []
15
18
  interpolated_x = []
16
19
  lerp(@last_f, @last_x, frame, float_x) do | interp_f, interp_x |
@@ -1,6 +1,11 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  # Prevents you from exporting invalid trackers
3
3
  class Tracksperanto::Middleware::Lint < Tracksperanto::Middleware::Base
4
+
5
+ def self.action_description
6
+ "Verify all the exported trackers and check for errors"
7
+ end
8
+
4
9
  class NoTrackersExportedError < RuntimeError
5
10
  def message
6
11
  "There were no trackers exported"
@@ -0,0 +1,50 @@
1
+ # -*- encoding : utf-8 -*-
2
+ # Sometimes your tracked sequence has been loaded from say frame 73282381, but you want to import into an application
3
+ # that expects the trackers to start at frame 1. This middleware autoslips everything so that your trackers start at frame 1.
4
+ class Tracksperanto::Middleware::MoveToFirst < Tracksperanto::Middleware::Base
5
+
6
+ def self.action_description
7
+ "Move all the keyframes in time so that the first frame is the first animated keyframe"
8
+ end
9
+
10
+ def start_export(width, height)
11
+ @width, @height = width, height
12
+ @start_frames = []
13
+ @buf = Obuf.new
14
+ @registered = false
15
+ end
16
+
17
+ def start_tracker_segment(tracker_name)
18
+ @current = Tracksperanto::Tracker.new(:name => tracker_name)
19
+ end
20
+
21
+ def end_tracker_segment
22
+ @buf.push(@current)
23
+ @registered = false
24
+ end
25
+
26
+ def export_point(frame, float_x, float_y, float_residual)
27
+ @current.keyframe!(:frame => frame, :abs_x => float_x, :abs_y => float_y, :residual => float_residual)
28
+ return if @registered
29
+
30
+ # Note where this tracker starts
31
+ @start_frames.push(frame)
32
+ @registered = true
33
+ end
34
+
35
+ def end_export
36
+ actual_start = @start_frames.sort.shift
37
+
38
+ @exporter.start_export(@width, @height)
39
+ @buf.each do | tracker |
40
+ @exporter.start_tracker_segment(tracker.name)
41
+ tracker.each do | kf |
42
+ @exporter.export_point(kf.frame - actual_start, kf.abs_x, kf.abs_y, kf.residual)
43
+ end
44
+ @exporter.end_tracker_segment
45
+ end
46
+ @exporter.end_export
47
+
48
+ @buf.clear
49
+ end
50
+ end
@@ -3,9 +3,15 @@
3
3
  # some fucked-up telecine transfers. The padding is in fractional units of the total width
4
4
  # and height
5
5
  class Tracksperanto::Middleware::Pad < Tracksperanto::Middleware::Base
6
- attr_accessor :left_pad, :right_pad, :top_pad, :bottom_pad
7
6
 
8
- cast_to_float :left_pad, :right_pad, :top_pad, :bottom_pad
7
+ parameter :top_pad, :cast => :float, :desc => "Top padding (fraction of original size)"
8
+ parameter :left_pad, :cast => :float, :desc => "Left padding (fraction of original size)"
9
+ parameter :right_pad, :cast => :float, :desc => "Right padding (fraction of original size)"
10
+ parameter :bottom_pad, :cast => :float, :desc => "Bottom padding (fraction of original size)"
11
+
12
+ def self.action_description
13
+ "Pad or crop the image by a fraction of it's original size"
14
+ end
9
15
 
10
16
  def start_export(w, h)
11
17
  @shift_left, @shift_bottom = w * left_pad, h * bottom_pad