class FlameChannelParser::Interpolator
Used to sample Flame animation curves. Pass a Channel object to the interpolator and you can then sample values at arbitrary frames.
i = Interpolator.new(parsed_channel) i.value_at(245.5) # => will interpolate and return the value at frame 245.5
Constants
- NEG_INF
- POS_INF
Public Class Methods
new(channel)
click to toggle source
The constructor will accept a ChannelBlock object and convert it internally to a number of segments from which samples can be made
# File lib/interpolator.rb, line 17 def initialize(channel) @segments = [] @extrap = channel.extrapolation # Edge case - channel has no anim at all @segments = if channel.length.zero? [ConstantFunction.new(channel.base_value)] else create_segments_from_channel(channel) end end
Public Instance Methods
first_defined_frame()
click to toggle source
Returns the first frame number that is concretely defined as a keyframe after the prepolation ends
# File lib/interpolator.rb, line 42 def first_defined_frame first_f = @segments[0].end_frame return 1 if first_f == NEG_INF return first_f end
last_defined_frame()
click to toggle source
Returns the last frame number that is concretely defined as a keyframe before the extrapolation starts
# File lib/interpolator.rb, line 50 def last_defined_frame last_f = @segments[-1].start_frame return 100 if last_f == POS_INF return last_f end
sample_at(frame)
click to toggle source
Sample the value of the animation curve at this frame
# File lib/interpolator.rb, line 30 def sample_at(frame) if :cycle == @extrap return sample_from_segments(frame_number_in_cycle(frame)) elsif :revcycle == @extrap return sample_from_segments(frame_number_in_revcycle(frame)) else sample_from_segments(frame) end end
Private Instance Methods
create_segments_from_channel(channel)
click to toggle source
# File lib/interpolator.rb, line 58 def create_segments_from_channel(channel) # First the prepolating segment segments = [pick_prepolation(channel.extrapolation, channel[0], channel[1])] # Then all the intermediate segments, one segment between each pair of keys channel[0..-2].each_with_index do | key, index | segments << key_pair_to_segment(key, channel[index + 1]) end # and the extrapolator segments << pick_extrapolation(channel.extrapolation, channel[-2], channel[-1]) end
frame_number_in_cycle(frame)
click to toggle source
# File lib/interpolator.rb, line 84 def frame_number_in_cycle(frame) animated_across = (last_defined_frame - first_defined_frame) offset = (frame - first_defined_frame) modulo = (offset % animated_across) first_defined_frame + modulo end
frame_number_in_revcycle(frame)
click to toggle source
# File lib/interpolator.rb, line 71 def frame_number_in_revcycle(frame) animated_across = (last_defined_frame - first_defined_frame) # Absolute offset from the first keyframe of the animated segment offset = (frame - first_defined_frame).abs absolute_unit = offset % animated_across cycles = (offset / animated_across).floor if (cycles % 2).zero? first_defined_frame + absolute_unit else last_defined_frame - absolute_unit end end
key_pair_to_segment(key, next_key)
click to toggle source
We need both the preceding and the next key
# File lib/interpolator.rb, line 129 def key_pair_to_segment(key, next_key) case key.interpolation when :bezier BezierSegment.new(key.frame, next_key.frame, key.value, next_key.value, key.r_handle_x, key.r_handle_y, next_key.l_handle_x, next_key.l_handle_y) when :natural, :hermite HermiteSegment.new(key.frame, next_key.frame, key.value, next_key.value, key.right_slope, next_key.left_slope) when :constant ConstantSegment.new(key.frame, next_key.frame, key.value) else # Linear and safe LinearSegment.new(key.frame, next_key.frame, key.value, next_key.value) end end
pick_extrapolation(extrap_symbol, previous_key, last_key)
click to toggle source
# File lib/interpolator.rb, line 112 def pick_extrapolation(extrap_symbol, previous_key, last_key) if extrap_symbol == :linear if previous_key && last_key.interpolation == :linear # For linear keys the tangent actually does not do anything, so we need to look a frame # ahead and compute the increment increment = (last_key.value - previous_key.value) / (last_key.frame - previous_key.frame) LinearExtrapolate.new(last_key.frame, last_key.value, increment) else LinearExtrapolate.new(last_key.frame, last_key.value, last_key.right_slope) end else ConstantExtrapolate.new(last_key.frame, last_key.value) end end
pick_prepolation(extrap_symbol, first_key, second_key)
click to toggle source
# File lib/interpolator.rb, line 97 def pick_prepolation(extrap_symbol, first_key, second_key) if extrap_symbol == :linear && second_key if first_key.interpolation != :linear LinearPrepolate.new(first_key.frame, first_key.value, first_key.left_slope) else # For linear keys the tangent actually does not do anything, so we need to look a frame # ahead and compute the increment increment = (second_key.value - first_key.value) / (second_key.frame - first_key.frame) LinearPrepolate.new(first_key.frame, first_key.value, increment) end else ConstantPrepolate.new(first_key.frame, first_key.value) end end
sample_from_segments(at_frame)
click to toggle source
# File lib/interpolator.rb, line 91 def sample_from_segments(at_frame) segment = @segments.find{|s| s.defines?(at_frame) } raise "No segment on this curve that can interpolate the value at #{frame}" unless segment segment.value_at(at_frame) end