lib/tracksperanto/tracker.rb in tracksperanto-2.8.6 vs lib/tracksperanto/tracker.rb in tracksperanto-2.9.0
- old
+ new
@@ -1,48 +1,132 @@
+# -*- encoding : utf-8 -*-
# Internal representation of a tracker point with keyframes. A Tracker is an array of Keyframe objects
# with a few methods added for convenience
-class Tracksperanto::Tracker < DelegateClass(Array)
- include Tracksperanto::Casts
- include Tracksperanto::BlockInit
- include Comparable
+class Tracksperanto::Tracker
+ include Tracksperanto::Casts, Tracksperanto::BlockInit, Comparable, Enumerable
# Contains the name of the tracker
attr_accessor :name
cast_to_string :name
+ class Dupe < RuntimeError
+ end
+
def initialize(object_attribute_hash = {})
@name = "Tracker"
- __setobj__(Array.new)
+ @frame_table = {}
super
end
+ # Replace all the keyframes of the tracker with new ones
def keyframes=(new_kf_array)
- __setobj__(new_kf_array.dup)
+ @frame_table = {}
+ new_kf_array.each do | keyframe |
+ @frame_table[keyframe.frame] = keyframe.abs_x, keyframe.abs_y, keyframe.residual
+ end
end
-
- alias_method :keyframes, :__getobj__
+ # Returns an array of keyframes, ordered by their frame value.
+ # WARNING: in older Tracksperanto versions the returned value was
+ # a handle into the tracker object. Now it returns a copy of the tracker's keyframes
+ # and modifications done to the array WILL NOT propagate to the tracker object itself.
+ # If you need to replace a keyframe, use set(keyframe). If you need to replace the whole
+ # keyframes array, use keyframes=(new_keyframes)
+ def keyframes
+ to_a
+ end
+
+ # Sets a keyframe. If an old keyframe exists at this frame offset it will be replaced.
+ def set(kf)
+ @frame_table[kf.frame] = [kf.abs_x, kf.abs_y, kf.residual]
+ end
+
+ # Iterates over keyframes
+ def each
+ ordered_frame_numbers.each do | frame |
+ yield(extract_keyframe(frame))
+ end
+ end
+
# Trackers sort by the position of the first keyframe
def <=>(other_tracker)
- self[0].frame <=> other_tracker[0].frame
+ self.first_frame <=> other_tracker.first_frame
end
+ # Returns the first frame number this tracker contains (where the first keyframe is)
+ def first_frame
+ ordered_frame_numbers[0]
+ end
+
# Automatically truncate spaces in the tracker
# name and replace them with underscores
def name=(n)
@name = n.to_s.gsub(/(\s+)/, '_')
end
- # Create and save a keyframe in this tracker
+ # Create and save a keyframe in this tracker. The options hash is the same
+ # as the one for the Keyframe constructor
def keyframe!(options)
- push(Tracksperanto::Keyframe.new(options))
+ kf = Tracksperanto::Keyframe.new(options)
+ set(kf)
end
+ # Tells whether this tracker is empty or not
+ def empty?
+ @frame_table.empty?
+ end
+
+ # Fetch a keyframe at a spefiic offset.
+ # NOTICE: not at a specific **frame** but at an offset in the frames table.
+ # The frames table is ordered by frame order. If you need to fetch a keyframe at a specific frame,
+ # use at_frame
+ def [](offset)
+ frame = ordered_frame_numbers[offset]
+ return nil if frame.nil?
+
+ extract_keyframe(frame)
+ end
+
+ # Fetch a keyframe at a specific frame. If no such frame exists nil will be returned
+ def at_frame(at_frame)
+ extract_keyframe(at_frame)
+ end
+
+ # Add a keyframe. Will raise a Dupe exception if the keyframe to be set will overwrite another one
+ def push(kf)
+ raise Dupe, "The tracker #{name.inspect} already contains a keyframe at #{kf.frame}" if @frame_table[kf.frame]
+ set(kf)
+ end
+
def inspect
"<T #{name.inspect} with #{length} keyframes>"
end
- # Hack - prevents the Tracker to be flattened into keyframes
- # when an Array of Trackers gets Array#flatten'ed
- def to_ary; end
- private :to_ary
+ # Used in tests
+ def to_ruby
+ buf = []
+ buf.push("Tracksperanto::Tracker.new(:name => %s) do |t|" % name.inspect)
+ each do | kf |
+ buf.push(" t.keyframe!(:frame => %d, :abs_x => %0.05f, :abs_y => %0.05f, :residual => %0.05f)" % [kf.frame, kf.abs_x, kf.abs_y, kf.residual])
+ end
+ buf.push("end")
+ buf.join("\n")
+ end
+
+ # Tells how many keyframes this tracker contains
+ def length
+ @frame_table.length
+ end
+
+ private
+
+ def ordered_frame_numbers
+ @frame_table.keys.sort
+ end
+
+ def extract_keyframe(frame)
+ triplet = @frame_table[frame]
+ return nil unless triplet
+
+ Tracksperanto::Keyframe.new(:frame => frame, :abs_x => triplet[0], :abs_y => triplet[1], :residual => triplet[2])
+ end
end
\ No newline at end of file