flame_channel_parser 1.4.0 → 2.0.1
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 +11 -2
- data/Manifest.txt +6 -2
- data/README.rdoc +1 -1
- data/Rakefile +2 -0
- data/bin/bake_flame_channel +26 -5
- data/lib/channel.rb +36 -0
- data/lib/extractor.rb +12 -2
- data/lib/flame_channel_parser.rb +6 -27
- data/lib/interpolator.rb +27 -24
- data/lib/key.rb +76 -0
- data/lib/parser.rb +115 -0
- data/test/test_channel.rb +22 -0
- data/test/test_cli.rb +59 -0
- data/test/test_flame_channel_parser.rb +1 -1
- data/test/test_interpolator.rb +4 -5
- data/test/test_key.rb +86 -0
- data/test/test_segments.rb +7 -2
- metadata +13 -27
- data/lib/parser_2011.rb +0 -156
- data/lib/parser_2012.rb +0 -59
data/History.txt
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
=== 2.0.1 / 2011-23-06
|
2
|
+
|
3
|
+
* Fix the bake_flame_channel binary to use the proper extractor API
|
4
|
+
* Add a test for the binary
|
5
|
+
|
6
|
+
=== 2.0.0 / 2011-08-06
|
7
|
+
|
8
|
+
* Remove most of the Array methods from the Channel object
|
9
|
+
|
1
10
|
=== 1.4.0 / 2011-06-06
|
2
11
|
|
3
12
|
* Implement cycle and reverse-cycle extrapolation
|
@@ -6,11 +15,11 @@
|
|
6
15
|
|
7
16
|
* Fix linear extrapolation occurring after linear keyframes, enforce the tangent/increment of zero for linear extrap. segments
|
8
17
|
|
9
|
-
=== 1.3.1 / 2011-05
|
18
|
+
=== 1.3.1 / 2011-30-05
|
10
19
|
|
11
20
|
* Add minor fixes and imporvements to the curve extraction bin
|
12
21
|
|
13
|
-
=== 1.3.0 / 2011-05
|
22
|
+
=== 1.3.0 / 2011-20-05
|
14
23
|
|
15
24
|
* Added the Extractor class that does simple per-frame baking to STDOUT
|
16
25
|
* Add bake_flame_channel binary
|
data/Manifest.txt
CHANGED
@@ -4,11 +4,12 @@ Manifest.txt
|
|
4
4
|
README.rdoc
|
5
5
|
Rakefile
|
6
6
|
bin/bake_flame_channel
|
7
|
+
lib/channel.rb
|
7
8
|
lib/extractor.rb
|
8
9
|
lib/flame_channel_parser.rb
|
9
10
|
lib/interpolator.rb
|
10
|
-
lib/
|
11
|
-
lib/
|
11
|
+
lib/key.rb
|
12
|
+
lib/parser.rb
|
12
13
|
lib/segments.rb
|
13
14
|
test/channel_with_constants.dat
|
14
15
|
test/sample_channel.dat
|
@@ -30,7 +31,10 @@ test/snaps/RefT_Steadicam_TwoKFs_HermiteAtBegin.timewarp
|
|
30
31
|
test/snaps/TW.timewarp
|
31
32
|
test/snaps/TW_SingleFrameExtrapolated_from2011.timewarp
|
32
33
|
test/snaps/timewarp_where_interp_fails_at_end.timewarp
|
34
|
+
test/test_channel.rb
|
35
|
+
test/test_cli.rb
|
33
36
|
test/test_extractor.rb
|
34
37
|
test/test_flame_channel_parser.rb
|
35
38
|
test/test_interpolator.rb
|
39
|
+
test/test_key.rb
|
36
40
|
test/test_segments.rb
|
data/README.rdoc
CHANGED
@@ -32,7 +32,7 @@ To use the library:
|
|
32
32
|
frame_channel = channels.find{|c| c.name == "Timing/Timing" }
|
33
33
|
|
34
34
|
# Grab the interpolator object for this channel.
|
35
|
-
interpolator =
|
35
|
+
interpolator = frame_channel.to_interpolator
|
36
36
|
|
37
37
|
# Now sample from frame 20 to frame 250.
|
38
38
|
# You can also sample at fractional frames if you want to.
|
data/Rakefile
CHANGED
@@ -4,6 +4,8 @@ require 'rubygems'
|
|
4
4
|
require 'hoe'
|
5
5
|
|
6
6
|
Hoe.spec 'flame_channel_parser' do | p |
|
7
|
+
Hoe::RUBY_FLAGS.gsub!(/^\-w/, '') # No thanks undefined ivar warnings
|
8
|
+
|
7
9
|
p.readme_file = 'README.rdoc'
|
8
10
|
p.extra_rdoc_files = FileList['*.rdoc'] + FileList['*.txt']
|
9
11
|
p.extra_deps = {"update_hints" => ">=0" }
|
data/bin/bake_flame_channel
CHANGED
@@ -1,8 +1,21 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
|
2
|
+
# == Synopsis
|
3
|
+
#
|
4
|
+
# Extract an animation channel from a Flame setup
|
5
|
+
#
|
6
|
+
# == Usage
|
7
|
+
#
|
8
|
+
# bake_flame_channel TW_015_v03.timewarp
|
9
|
+
#
|
10
|
+
# == Author
|
11
|
+
# Julik <me@julik.nl>
|
12
|
+
|
13
|
+
require File.expand_path(File.dirname(__FILE__)) + '/../lib/flame_channel_parser' unless defined?(FlameChannelParser)
|
3
14
|
require 'optparse'
|
15
|
+
require 'rubygems'
|
16
|
+
require "update_hints"
|
4
17
|
|
5
|
-
options = {}
|
18
|
+
options = {:destination => $stdout}
|
6
19
|
|
7
20
|
op = OptionParser.new
|
8
21
|
op.banner = "Usage: bake_flame_channel --channel Timing/Timing -e 123 /usr/discreet/projects/Luxury/timewarp/shot2_tw.timewarp > /mnt/3d/curves/shot2_tw.framecurve.txt\n" +
|
@@ -23,9 +36,17 @@ op.on(" -f", "--to-file FILENAME", String,
|
|
23
36
|
|
24
37
|
op.parse!
|
25
38
|
setup_path = ARGV.shift
|
26
|
-
|
39
|
+
unless setup_path
|
40
|
+
$stderr.puts "No input file path provided. Please see bake_flame_channel --help for usage information."
|
41
|
+
exit -1
|
42
|
+
end
|
43
|
+
|
44
|
+
unless File.exist?(setup_path)
|
45
|
+
$stderr.puts "File does not exist."
|
46
|
+
exit -1
|
47
|
+
end
|
27
48
|
|
28
|
-
FlameChannelParser::Extractor.
|
49
|
+
FlameChannelParser::Extractor.extract(setup_path, options)
|
29
50
|
options[:destination].close if options[:destination].respond_to?(:close)
|
30
51
|
|
31
|
-
UpdateHints.version_check("flame_channel_parser", FlameChannelParser::VERSION)
|
52
|
+
UpdateHints.version_check("flame_channel_parser", FlameChannelParser::VERSION, $stderr)
|
data/lib/channel.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
|
3
|
+
module FlameChannelParser
|
4
|
+
# Represents a channel parsed from the Flame setup. Contains
|
5
|
+
# the channel metadata and keyframes (Key objects).
|
6
|
+
# Supports the following standard Array methods:
|
7
|
+
# :empty?, :size, :each, :[], :push
|
8
|
+
class Channel
|
9
|
+
include Enumerable
|
10
|
+
extend Forwardable
|
11
|
+
|
12
|
+
attr_reader :node_type, :node_name
|
13
|
+
attr_accessor :base_value, :name, :extrapolation
|
14
|
+
|
15
|
+
def_delegators :@keys, :empty?, :length, :each, :[], :push, :<<
|
16
|
+
|
17
|
+
def initialize(channel_name, node_type, node_name)
|
18
|
+
@keys = []
|
19
|
+
@node_type, @node_name, @name = node_type, node_name, channel_name.strip
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns path to the channel (like axis1/position/x)
|
23
|
+
def path
|
24
|
+
[@node_name, name].compact.join("/")
|
25
|
+
end
|
26
|
+
|
27
|
+
# Get an Interpolator for this channel
|
28
|
+
def to_interpolator
|
29
|
+
FlameChannelParser::Interpolator.new(self)
|
30
|
+
end
|
31
|
+
|
32
|
+
def inspect
|
33
|
+
"<Channel (%s %s) with %d keys>" % [@node_type, path, @keys.size]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/extractor.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Extracts and bakes a specific animation channel
|
1
|
+
# Extracts and bakes a specific animation channel to a given buffer, one string per frame
|
2
2
|
class FlameChannelParser::Extractor
|
3
3
|
|
4
4
|
DEFAULT_CHANNEL_TO_EXTRACT = "Timing/Timing"
|
@@ -20,6 +20,12 @@ class FlameChannelParser::Extractor
|
|
20
20
|
# :start_frame - From which frame the curve should be baked. Will default to the first keyframe of the curve
|
21
21
|
# :end_frame - Upto which frame to bake. Will default to the last keyframe of the curve
|
22
22
|
# :channel - Name of the channel to extract from the setup. Defaults to "Timing/Timing" (timewarp frame)
|
23
|
+
#
|
24
|
+
# Note that start_frame and end_frame will be converted to integers.
|
25
|
+
# The output will look like this:
|
26
|
+
#
|
27
|
+
# 1 123.456
|
28
|
+
# 2 124.567
|
23
29
|
def self.extract(path, options = {})
|
24
30
|
options = DEFAULTS.merge(options)
|
25
31
|
File.open(path) do |f|
|
@@ -47,6 +53,7 @@ class FlameChannelParser::Extractor
|
|
47
53
|
|
48
54
|
from_frame = start_frame || interpolator.first_defined_frame
|
49
55
|
to_frame = end_frame || interpolator.last_defined_frame
|
56
|
+
|
50
57
|
unless (from_frame && to_frame)
|
51
58
|
raise NoKeyframesError, "This channel probably has no animation so there is no way to automatically tell how many keyframes it has. " +
|
52
59
|
"Please set the start and end frame explicitly."
|
@@ -54,7 +61,10 @@ class FlameChannelParser::Extractor
|
|
54
61
|
|
55
62
|
raise EmptySegmentError, "The segment you are trying to bake is too small (it has nothing in it)" if to_frame - from_frame < 1
|
56
63
|
|
57
|
-
|
64
|
+
from_frame_i = from_frame.to_f.floor
|
65
|
+
to_frame_i = to_frame.to_f.ceil
|
66
|
+
|
67
|
+
(from_frame_i..to_frame_i).each do | frame |
|
58
68
|
line = "%d\t%.5f\n" % [frame, interpolator.sample_at(frame)]
|
59
69
|
to_io << line
|
60
70
|
end
|
data/lib/flame_channel_parser.rb
CHANGED
@@ -1,33 +1,12 @@
|
|
1
|
-
require "delegate"
|
2
|
-
|
3
1
|
module FlameChannelParser
|
4
|
-
VERSION = '
|
2
|
+
VERSION = '2.0.1'
|
5
3
|
|
6
|
-
# Parse a Flame setup into an array of
|
4
|
+
# Parse a Flame setup into an array of Channel
|
7
5
|
def self.parse(io)
|
8
|
-
|
9
|
-
parser_class.new.parse(io)
|
10
|
-
end
|
11
|
-
|
12
|
-
private
|
13
|
-
|
14
|
-
def self.detect_parser_class_from(io)
|
15
|
-
# Scan the IO
|
16
|
-
until io.eof?
|
17
|
-
str = io.gets
|
18
|
-
if str =~ /RHandleX/ # Flame 2012, use that parser
|
19
|
-
io.rewind
|
20
|
-
return Parser2012
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
io.rewind
|
25
|
-
Parser2011
|
6
|
+
Parser.new.parse(io)
|
26
7
|
end
|
27
8
|
end
|
28
9
|
|
29
|
-
|
30
|
-
|
31
|
-
require File.expand_path(File.dirname(__FILE__)
|
32
|
-
require File.expand_path(File.dirname(__FILE__)) + "/interpolator"
|
33
|
-
require File.expand_path(File.dirname(__FILE__)) + "/extractor"
|
10
|
+
%w(
|
11
|
+
key channel parser segments interpolator extractor
|
12
|
+
).each {|f| require File.expand_path(File.dirname(__FILE__) + "/" + f ) }
|
data/lib/interpolator.rb
CHANGED
@@ -5,7 +5,7 @@ require File.expand_path(File.dirname(__FILE__)) + "/segments"
|
|
5
5
|
# frames.
|
6
6
|
#
|
7
7
|
# i = Interpolator.new(parsed_channel)
|
8
|
-
# i.value_at(245.5) # => will interpolate and return the value
|
8
|
+
# i.value_at(245.5) # => will interpolate and return the value at frame 245.5
|
9
9
|
class FlameChannelParser::Interpolator
|
10
10
|
include FlameChannelParser::Segments
|
11
11
|
|
@@ -19,20 +19,10 @@ class FlameChannelParser::Interpolator
|
|
19
19
|
@extrap = channel.extrapolation
|
20
20
|
|
21
21
|
# Edge case - channel has no anim at all
|
22
|
-
if channel.length.zero?
|
23
|
-
|
22
|
+
@segments = if channel.length.zero?
|
23
|
+
[ConstantFunction.new(channel.base_value)]
|
24
24
|
else
|
25
|
-
|
26
|
-
# First the prepolating segment
|
27
|
-
@segments << pick_prepolation(channel.extrapolation, channel[0], channel[1])
|
28
|
-
|
29
|
-
# Then all the intermediate segments, one segment between each pair of keys
|
30
|
-
channel[0..-2].each_with_index do | key, index |
|
31
|
-
@segments << key_pair_to_segment(key, channel[index + 1])
|
32
|
-
end
|
33
|
-
|
34
|
-
# so we just output it separately
|
35
|
-
@segments << pick_extrapolation(channel.extrapolation, channel[-2], channel[-1])
|
25
|
+
create_segments_from_channel(channel)
|
36
26
|
end
|
37
27
|
end
|
38
28
|
|
@@ -65,24 +55,37 @@ class FlameChannelParser::Interpolator
|
|
65
55
|
|
66
56
|
private
|
67
57
|
|
58
|
+
def create_segments_from_channel(channel)
|
59
|
+
# First the prepolating segment
|
60
|
+
segments = [pick_prepolation(channel.extrapolation, channel[0], channel[1])]
|
61
|
+
|
62
|
+
# Then all the intermediate segments, one segment between each pair of keys
|
63
|
+
channel[0..-2].each_with_index do | key, index |
|
64
|
+
segments << key_pair_to_segment(key, channel[index + 1])
|
65
|
+
end
|
66
|
+
|
67
|
+
# and the extrapolator
|
68
|
+
segments << pick_extrapolation(channel.extrapolation, channel[-2], channel[-1])
|
69
|
+
end
|
70
|
+
|
68
71
|
def frame_number_in_revcycle(frame)
|
69
72
|
animated_across = (last_defined_frame - first_defined_frame)
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
if (
|
75
|
-
first_defined_frame +
|
73
|
+
# Absolute offset from the first keyframe of the animated segment
|
74
|
+
offset = (frame - first_defined_frame).abs
|
75
|
+
absolute_unit = offset % animated_across
|
76
|
+
cycles = (offset / animated_across).floor
|
77
|
+
if (cycles % 2).zero?
|
78
|
+
first_defined_frame + absolute_unit
|
76
79
|
else
|
77
|
-
last_defined_frame -
|
80
|
+
last_defined_frame - absolute_unit
|
78
81
|
end
|
79
82
|
end
|
80
83
|
|
81
84
|
def frame_number_in_cycle(frame)
|
82
85
|
animated_across = (last_defined_frame - first_defined_frame)
|
83
|
-
|
84
|
-
|
85
|
-
first_defined_frame +
|
86
|
+
offset = (frame - first_defined_frame)
|
87
|
+
modulo = (offset % animated_across)
|
88
|
+
first_defined_frame + modulo
|
86
89
|
end
|
87
90
|
|
88
91
|
def sample_from_segments(at_frame)
|
data/lib/key.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
# Represents a keyframe
|
2
|
+
class FlameChannelParser::Key
|
3
|
+
|
4
|
+
# Frame on which the keyframe is set
|
5
|
+
attr_accessor :frame
|
6
|
+
|
7
|
+
# Value at this keyframe
|
8
|
+
attr_accessor :value
|
9
|
+
|
10
|
+
# Inteprolation type from this key onwards
|
11
|
+
attr_accessor :interpolation
|
12
|
+
|
13
|
+
# Curve order (relevant for 2012 only)
|
14
|
+
attr_accessor :curve_order, :curve_mode
|
15
|
+
|
16
|
+
# Left and right slope (will return raw slope values for pre-2012, and computed ones for 2012)
|
17
|
+
attr_accessor :left_slope, :right_slope
|
18
|
+
|
19
|
+
# Whether the tangents are broken at this keyframe
|
20
|
+
attr_accessor :break_slope
|
21
|
+
|
22
|
+
# Coordinates of the handles for 2012 setups
|
23
|
+
attr_accessor :l_handle_x, :l_handle_y, :r_handle_x, :r_handle_y
|
24
|
+
|
25
|
+
# Default value is 0.f
|
26
|
+
def value
|
27
|
+
@value.to_f
|
28
|
+
end
|
29
|
+
|
30
|
+
# Default frame is 1.0f
|
31
|
+
def frame
|
32
|
+
(@frame || 1).to_f
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns the RightSlope parameter of the keyframe which we use for interpolations
|
36
|
+
def left_slope
|
37
|
+
return right_slope unless break_slope
|
38
|
+
|
39
|
+
if has_2012_tangents? # 2012 setups do not have slopes but have tangents
|
40
|
+
dy = @value - @l_handle_y
|
41
|
+
dx = @l_handle_x.to_f - @frame
|
42
|
+
(dy / dx * -1)
|
43
|
+
else
|
44
|
+
@left_slope.to_f
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns the LeftSlope parameter of the keyframe which we use for interpolations
|
49
|
+
def right_slope
|
50
|
+
if has_2012_tangents?
|
51
|
+
dy = @value - @r_handle_y
|
52
|
+
dx = @frame.to_f - @r_handle_x
|
53
|
+
dy / dx
|
54
|
+
else
|
55
|
+
(@right_slope || nil).to_f
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Tells if this keyframe has 2012 tangents in it
|
60
|
+
def has_2012_tangents?
|
61
|
+
@has_tangents ||= !!(l_handle_x && l_handle_y)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Adapter for old interpolation
|
65
|
+
def interpolation
|
66
|
+
# Just return the interpolation type for pre-2012 setups
|
67
|
+
return (@interpolation || :constant) unless has_2012_tangents?
|
68
|
+
|
69
|
+
return :constant if curve_order.to_s == "constant"
|
70
|
+
return :hermite if curve_order.to_s == "cubic" && (curve_mode.to_s == "hermite" || curve_mode.to_s == "natural")
|
71
|
+
return :bezier if curve_order.to_s == "cubic" && curve_mode.to_s == "bezier"
|
72
|
+
return :linear if curve_order.to_s == "linear"
|
73
|
+
|
74
|
+
raise "Cannot determine interpolation for #{inspect}"
|
75
|
+
end
|
76
|
+
end
|
data/lib/parser.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
|
3
|
+
# Basic parser used for setups from versions up to 2011
|
4
|
+
module FlameChannelParser
|
5
|
+
class Parser
|
6
|
+
# Parses the setup passed in the IO
|
7
|
+
def parse(io)
|
8
|
+
channels = []
|
9
|
+
node_name, node_type = nil, nil
|
10
|
+
|
11
|
+
until io.eof?
|
12
|
+
line = io.gets
|
13
|
+
if line =~ NODE_TYPE_MATCHER
|
14
|
+
node_type = $1
|
15
|
+
elsif line =~ NODE_NAME_MATCHER
|
16
|
+
node_name = $1
|
17
|
+
elsif line =~ CHANNEL_MATCHER && channel_is_useful?($1)
|
18
|
+
channels << parse_channel(io, $1, node_type, node_name)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
channels
|
23
|
+
end
|
24
|
+
|
25
|
+
# Override this method to skip some channels, this will speedup
|
26
|
+
# your code alot
|
27
|
+
def channel_is_useful?(channel_name)
|
28
|
+
true
|
29
|
+
end
|
30
|
+
|
31
|
+
# Defines a number of regular expression matchers applied to the file as it is being parsed
|
32
|
+
def key_matchers #:nodoc:
|
33
|
+
[
|
34
|
+
# Previously:
|
35
|
+
|
36
|
+
[:frame, :to_f, /Frame ([\-\d\.]+)/],
|
37
|
+
[:value, :to_f, /Value ([\-\d\.]+)/],
|
38
|
+
[:left_slope, :to_f, /LeftSlope ([\-\d\.]+)/],
|
39
|
+
[:right_slope, :to_f, /RightSlope ([\-\d\.]+)/],
|
40
|
+
[:interpolation, :to_s, /Interpolation (\w+)/],
|
41
|
+
[:break_slope, :to_s, /BreakSlope (\w+)/],
|
42
|
+
|
43
|
+
# 2012 intoroduces:
|
44
|
+
|
45
|
+
[:r_handle_x, :to_f, /RHandleX ([\-\d\.]+)/],
|
46
|
+
[:l_handle_x, :to_f, /LHandleX ([\-\d\.]+)/],
|
47
|
+
[:r_handle_y, :to_f, /RHandleY ([\-\d\.]+)/],
|
48
|
+
[:l_handle_y, :to_f, /LHandleY ([\-\d\.]+)/],
|
49
|
+
[:curve_mode, :to_s, /CurveMode (\w+)/],
|
50
|
+
[:curve_order, :to_s, /CurveOrder (\w+)/],
|
51
|
+
]
|
52
|
+
end
|
53
|
+
base_value_matcher = /Value ([\-\d\.]+)/
|
54
|
+
keyframe_count_matcher = /Size (\d+)/
|
55
|
+
|
56
|
+
BASE_VALUE_MATCHER = /Value ([\-\d\.]+)/
|
57
|
+
KF_COUNT_MATCHER = /Size (\d+)/
|
58
|
+
EXTRAP_MATCHER = /Extrapolation (\w+)/
|
59
|
+
CHANNEL_MATCHER = /Channel (.+)\n/
|
60
|
+
NODE_TYPE_MATCHER = /Node (\w+)/
|
61
|
+
NODE_NAME_MATCHER = /Name (\w+)/
|
62
|
+
LITERALS = %w( linear constant natural hermite cubic bezier cycle revcycle )
|
63
|
+
|
64
|
+
def parse_channel(io, channel_name, node_type, node_name)
|
65
|
+
c = Channel.new(channel_name, node_type, node_name)
|
66
|
+
|
67
|
+
indent, end_mark = nil, "ENDMARK"
|
68
|
+
|
69
|
+
while line = io.gets
|
70
|
+
|
71
|
+
unless indent
|
72
|
+
indent = line.scan(/^(\s+)/)[1]
|
73
|
+
end_mark = "#{indent}End"
|
74
|
+
end
|
75
|
+
|
76
|
+
if line =~ KF_COUNT_MATCHER
|
77
|
+
$1.to_i.times { c.push(extract_key_from(io)) }
|
78
|
+
elsif line =~ BASE_VALUE_MATCHER# && empty?
|
79
|
+
c.base_value = $1.to_f
|
80
|
+
elsif line =~ EXTRAP_MATCHER
|
81
|
+
c.extrapolation = symbolize_literal($1)
|
82
|
+
elsif line.strip == end_mark
|
83
|
+
break
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
return c
|
88
|
+
end
|
89
|
+
|
90
|
+
def extract_key_from(io)
|
91
|
+
frame = nil
|
92
|
+
end_matcher = /End/
|
93
|
+
key = Key.new
|
94
|
+
|
95
|
+
until io.eof?
|
96
|
+
line = io.gets
|
97
|
+
if line =~ end_matcher
|
98
|
+
return key
|
99
|
+
else
|
100
|
+
key_matchers.each do | property, cast_method, pattern |
|
101
|
+
if line =~ pattern
|
102
|
+
v = symbolize_literal($1.send(cast_method))
|
103
|
+
key.send("#{property}=", v)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
raise "Did not detect any keyframes!"
|
109
|
+
end
|
110
|
+
|
111
|
+
def symbolize_literal(v)
|
112
|
+
LITERALS.include?(v) ? v.to_sym : v
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
require File.dirname(__FILE__) + "/../lib/flame_channel_parser"
|
3
|
+
|
4
|
+
D = 0.001
|
5
|
+
|
6
|
+
class TestChannel < Test::Unit::TestCase
|
7
|
+
def test_responds_to_array_methods
|
8
|
+
c = FlameChannelParser::Channel.new("b/c", "d", "f")
|
9
|
+
[:empty?, :length, :each, :[], :push, :<<].each {|m| assert c.respond_to?(m) }
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_path_for_present_nodename
|
13
|
+
c = FlameChannelParser::Channel.new("position/x", "Axis", "axis1")
|
14
|
+
assert_equal "axis1/position/x", c.path
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_path_for_absent
|
18
|
+
c = FlameChannelParser::Channel.new("Frame/Frame", nil, nil)
|
19
|
+
assert_equal "Frame/Frame", c.path
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
data/test/test_cli.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
require "stringio"
|
3
|
+
require "tempfile"
|
4
|
+
|
5
|
+
require File.dirname(__FILE__) + "/../lib/flame_channel_parser"
|
6
|
+
|
7
|
+
class CliTest < Test::Unit::TestCase
|
8
|
+
BIN_P = File.expand_path(File.dirname(__FILE__) + "/../bin/bake_flame_channel")
|
9
|
+
|
10
|
+
# Run the binary under test with passed options, and return [exit_code, stdout_content, stderr_content]
|
11
|
+
def cli(commandline_arguments)
|
12
|
+
old_stdout, old_stderr, old_argv = $stdout, $stderr, ARGV.dup
|
13
|
+
os, es = StringIO.new, StringIO.new
|
14
|
+
begin
|
15
|
+
$stdout, $stderr, verbosity = os, es, $VERBOSE
|
16
|
+
ARGV.replace(commandline_arguments.split)
|
17
|
+
$VERBOSE = false
|
18
|
+
load(BIN_P)
|
19
|
+
return [0, os.string, es.string]
|
20
|
+
rescue SystemExit => boom # The binary uses exit(), we use that to preserve the output code
|
21
|
+
return [boom.status, os.string, es.string]
|
22
|
+
ensure
|
23
|
+
$VERBOSE = verbosity
|
24
|
+
ARGV.replace(old_argv)
|
25
|
+
$stdout, $stderr = old_stdout, old_stderr
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_cli_with_no_args_produces_usage
|
30
|
+
status, o, e = cli('')
|
31
|
+
assert_equal -1, status
|
32
|
+
assert_match /No input file path provided/, e
|
33
|
+
assert_match /--help for usage information/, e
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_cli_with_nonexisting_file
|
37
|
+
status, o, e = cli(" amazing.action")
|
38
|
+
assert_equal -1, status
|
39
|
+
assert_match /does not exist/, e
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_cli_with_proper_output
|
43
|
+
full_path = File.expand_path(File.dirname(__FILE__)) + "/snaps/TW.timewarp"
|
44
|
+
status, output, e = cli(" " + full_path)
|
45
|
+
assert_equal 0, status
|
46
|
+
assert_equal 816, output.split("\n").length, "Should have output 816 frames"
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_cli_with_output_to_file
|
50
|
+
tf = Tempfile.new("experiment")
|
51
|
+
full_path = File.expand_path(File.dirname(__FILE__)) + "/snaps/TW.timewarp"
|
52
|
+
status, output, e = cli(" --to-file " + tf.path + " " + full_path)
|
53
|
+
|
54
|
+
assert_equal 0, status
|
55
|
+
assert_equal 816, File.read(tf.path).split("\n").length, "Should have output 816 frames"
|
56
|
+
ensure
|
57
|
+
tf.close!
|
58
|
+
end
|
59
|
+
end
|
@@ -18,7 +18,7 @@ class TestFlameChannelParser < Test::Unit::TestCase
|
|
18
18
|
assert_kind_of Array, channels
|
19
19
|
assert_equal 1, channels.length, "Should find one channel"
|
20
20
|
|
21
|
-
assert_kind_of FlameChannelParser::
|
21
|
+
assert_kind_of FlameChannelParser::Channel, channels[0]
|
22
22
|
|
23
23
|
ch = channels[0]
|
24
24
|
assert_equal 4, ch.length
|
data/test/test_interpolator.rb
CHANGED
@@ -57,11 +57,11 @@ class TestInterpolator < Test::Unit::TestCase
|
|
57
57
|
|
58
58
|
def test_simple_setup_from_2011
|
59
59
|
data = File.open(File.dirname(__FILE__) + "/snaps/FLEM_curves_example.action")
|
60
|
-
channels_in_action = FlameChannelParser
|
60
|
+
channels_in_action = FlameChannelParser.parse(data)
|
61
61
|
channels_in_action.reject!{|c| c.length < 4 }
|
62
62
|
|
63
|
-
reference = channels_in_action.find{|c| c.
|
64
|
-
sampled = channels_in_action.find{|c| c.
|
63
|
+
reference = channels_in_action.find{|c| c.path == "axis1/position/x" }
|
64
|
+
sampled = channels_in_action.find{|c| c.path == "axis1/position/y"}
|
65
65
|
assert_same_interpolation(1..200, reference, sampled)
|
66
66
|
end
|
67
67
|
|
@@ -121,7 +121,7 @@ class TestInterpolator < Test::Unit::TestCase
|
|
121
121
|
channels_in_tw = FlameChannelParser.parse(data)
|
122
122
|
chan = channels_in_tw.find{|c| c.name == "Timing/Timing"}
|
123
123
|
interp = chan.to_interpolator
|
124
|
-
assert_in_delta -7, interp.sample_at(1), DELTA
|
124
|
+
assert_in_delta( -7, interp.sample_at(1), DELTA)
|
125
125
|
assert_in_delta 492, interp.sample_at(502), DELTA
|
126
126
|
assert_in_delta 492, interp.sample_at(502), DELTA
|
127
127
|
assert_in_delta 494, interp.sample_at(504), DELTA
|
@@ -159,7 +159,6 @@ class TestInterpolator < Test::Unit::TestCase
|
|
159
159
|
assert_in_delta 683.772, interp.sample_at(1), DELTA
|
160
160
|
end
|
161
161
|
|
162
|
-
|
163
162
|
def test_cycle_extrapolation
|
164
163
|
data = File.open(File.dirname(__FILE__) + "/snaps/Cycle_and_revcycle.action")
|
165
164
|
channels_in_ac = FlameChannelParser.parse(data)
|
data/test/test_key.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
require File.dirname(__FILE__) + "/../lib/flame_channel_parser"
|
3
|
+
|
4
|
+
class TestKey < Test::Unit::TestCase
|
5
|
+
D = 0.001
|
6
|
+
def test_default_values
|
7
|
+
k = FlameChannelParser::Key.new
|
8
|
+
assert_in_delta 1.0, k.frame, D
|
9
|
+
assert_in_delta 0.0, k.value, D
|
10
|
+
assert_equal :constant, k.interpolation
|
11
|
+
assert_equal false, k.has_2012_tangents?
|
12
|
+
|
13
|
+
assert_nil k.l_handle_x
|
14
|
+
assert_nil k.l_handle_y
|
15
|
+
assert_nil k.r_handle_y
|
16
|
+
assert_nil k.r_handle_x
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_constant_curve_mode_translated_to_interpolation
|
20
|
+
k = FlameChannelParser::Key.new
|
21
|
+
k.curve_mode = :constant
|
22
|
+
assert_equal :constant, k.interpolation
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_hermite_curve_mode_translated_to_interpolation
|
26
|
+
k = FlameChannelParser::Key.new
|
27
|
+
assign_tangents_to(k)
|
28
|
+
|
29
|
+
k.curve_order = :cubic
|
30
|
+
k.curve_mode = :hermite
|
31
|
+
assert_equal :hermite, k.interpolation
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_natural_curve_mode_translated_to_interpolation
|
35
|
+
k = FlameChannelParser::Key.new
|
36
|
+
assign_tangents_to(k)
|
37
|
+
|
38
|
+
k.curve_order = :cubic
|
39
|
+
k.curve_mode = :natural
|
40
|
+
assert_equal :hermite, k.interpolation
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_bezier_curve_mode
|
44
|
+
k = FlameChannelParser::Key.new
|
45
|
+
assign_tangents_to(k)
|
46
|
+
|
47
|
+
k.curve_order = :cubic
|
48
|
+
k.curve_mode = :bezier
|
49
|
+
assert_equal :bezier, k.interpolation
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_tangent_computed_from_right_handle
|
53
|
+
k = FlameChannelParser::Key.new
|
54
|
+
k.frame = 123
|
55
|
+
k.value = 123
|
56
|
+
|
57
|
+
k.l_handle_x = 120
|
58
|
+
k.l_handle_y = 110
|
59
|
+
k.r_handle_x = 124
|
60
|
+
k.r_handle_y = 145
|
61
|
+
|
62
|
+
assert_in_delta 22, k.right_slope, D
|
63
|
+
assert_in_delta 22, k.left_slope, D
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_tangent_computed_from_right_handle_with_broken_slope
|
67
|
+
k = FlameChannelParser::Key.new
|
68
|
+
k.frame = 123
|
69
|
+
k.value = 123
|
70
|
+
k.break_slope = true
|
71
|
+
|
72
|
+
k.l_handle_x = 120
|
73
|
+
k.l_handle_y = 110
|
74
|
+
k.r_handle_x = 124
|
75
|
+
k.r_handle_y = 145
|
76
|
+
|
77
|
+
assert_in_delta 22, k.right_slope, D
|
78
|
+
assert_in_delta 4.333, k.left_slope, D
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def assign_tangents_to(to_key)
|
84
|
+
to_key.l_handle_x = to_key.l_handle_y = to_key.r_handle_x = to_key.r_handle_y = 1
|
85
|
+
end
|
86
|
+
end
|
data/test/test_segments.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
require "test/unit"
|
2
2
|
require File.dirname(__FILE__) + "/../lib/flame_channel_parser"
|
3
3
|
|
4
|
-
D = 0.001
|
5
4
|
|
6
5
|
include FlameChannelParser::Segments
|
7
6
|
|
8
7
|
class TestConstantFunction < Test::Unit::TestCase
|
8
|
+
D = 0.001
|
9
9
|
def test_segment
|
10
10
|
seg = ConstantFunction.new(123.4)
|
11
11
|
assert seg.defines?(-1), "The segment should define this frame"
|
@@ -16,6 +16,7 @@ end
|
|
16
16
|
|
17
17
|
|
18
18
|
class TestConstantSegment < Test::Unit::TestCase
|
19
|
+
D = 0.001
|
19
20
|
def test_segment
|
20
21
|
seg = ConstantSegment.new(12, 25, 2.5)
|
21
22
|
|
@@ -31,7 +32,7 @@ class TestConstantSegment < Test::Unit::TestCase
|
|
31
32
|
end
|
32
33
|
|
33
34
|
class TestBezierSegment < Test::Unit::TestCase
|
34
|
-
|
35
|
+
D = 0.001
|
35
36
|
def test_segment
|
36
37
|
seg = BezierSegment.new(
|
37
38
|
frame_from = 117,
|
@@ -54,6 +55,8 @@ class TestBezierSegment < Test::Unit::TestCase
|
|
54
55
|
end
|
55
56
|
|
56
57
|
class TestLinearSegment < Test::Unit::TestCase
|
58
|
+
D = 0.001
|
59
|
+
|
57
60
|
def test_segment
|
58
61
|
seg = LinearSegment.new(12, 25, 2.5, 4.5)
|
59
62
|
|
@@ -80,6 +83,8 @@ class TestConstantPrepolate < Test::Unit::TestCase
|
|
80
83
|
end
|
81
84
|
|
82
85
|
class TestHermiteSegment < Test::Unit::TestCase
|
86
|
+
D = 0.001
|
87
|
+
|
83
88
|
def test_last_curve_segment
|
84
89
|
refdata = %w(
|
85
90
|
258.239
|
metadata
CHANGED
@@ -1,13 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flame_channel_parser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash: 7
|
5
4
|
prerelease:
|
6
|
-
|
7
|
-
- 1
|
8
|
-
- 4
|
9
|
-
- 0
|
10
|
-
version: 1.4.0
|
5
|
+
version: 2.0.1
|
11
6
|
platform: ruby
|
12
7
|
authors:
|
13
8
|
- Julik Tarkhanov
|
@@ -15,8 +10,7 @@ autorequire:
|
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
12
|
|
18
|
-
date: 2011-06-
|
19
|
-
default_executable:
|
13
|
+
date: 2011-06-23 00:00:00 Z
|
20
14
|
dependencies:
|
21
15
|
- !ruby/object:Gem::Dependency
|
22
16
|
name: update_hints
|
@@ -26,9 +20,6 @@ dependencies:
|
|
26
20
|
requirements:
|
27
21
|
- - ">="
|
28
22
|
- !ruby/object:Gem::Version
|
29
|
-
hash: 3
|
30
|
-
segments:
|
31
|
-
- 0
|
32
23
|
version: "0"
|
33
24
|
type: :runtime
|
34
25
|
version_requirements: *id001
|
@@ -40,12 +31,7 @@ dependencies:
|
|
40
31
|
requirements:
|
41
32
|
- - ">="
|
42
33
|
- !ruby/object:Gem::Version
|
43
|
-
|
44
|
-
segments:
|
45
|
-
- 2
|
46
|
-
- 9
|
47
|
-
- 1
|
48
|
-
version: 2.9.1
|
34
|
+
version: 2.9.4
|
49
35
|
type: :development
|
50
36
|
version_requirements: *id002
|
51
37
|
description: |-
|
@@ -68,11 +54,12 @@ files:
|
|
68
54
|
- README.rdoc
|
69
55
|
- Rakefile
|
70
56
|
- bin/bake_flame_channel
|
57
|
+
- lib/channel.rb
|
71
58
|
- lib/extractor.rb
|
72
59
|
- lib/flame_channel_parser.rb
|
73
60
|
- lib/interpolator.rb
|
74
|
-
- lib/
|
75
|
-
- lib/
|
61
|
+
- lib/key.rb
|
62
|
+
- lib/parser.rb
|
76
63
|
- lib/segments.rb
|
77
64
|
- test/channel_with_constants.dat
|
78
65
|
- test/sample_channel.dat
|
@@ -94,12 +81,14 @@ files:
|
|
94
81
|
- test/snaps/TW.timewarp
|
95
82
|
- test/snaps/TW_SingleFrameExtrapolated_from2011.timewarp
|
96
83
|
- test/snaps/timewarp_where_interp_fails_at_end.timewarp
|
84
|
+
- test/test_channel.rb
|
85
|
+
- test/test_cli.rb
|
97
86
|
- test/test_extractor.rb
|
98
87
|
- test/test_flame_channel_parser.rb
|
99
88
|
- test/test_interpolator.rb
|
89
|
+
- test/test_key.rb
|
100
90
|
- test/test_segments.rb
|
101
91
|
- .gemtest
|
102
|
-
has_rdoc: true
|
103
92
|
homepage: http://guerilla-di.org/flame-channel-parser
|
104
93
|
licenses: []
|
105
94
|
|
@@ -114,28 +103,25 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
114
103
|
requirements:
|
115
104
|
- - ">="
|
116
105
|
- !ruby/object:Gem::Version
|
117
|
-
hash: 3
|
118
|
-
segments:
|
119
|
-
- 0
|
120
106
|
version: "0"
|
121
107
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
122
108
|
none: false
|
123
109
|
requirements:
|
124
110
|
- - ">="
|
125
111
|
- !ruby/object:Gem::Version
|
126
|
-
hash: 3
|
127
|
-
segments:
|
128
|
-
- 0
|
129
112
|
version: "0"
|
130
113
|
requirements: []
|
131
114
|
|
132
115
|
rubyforge_project: flame_channel_parser
|
133
|
-
rubygems_version: 1.
|
116
|
+
rubygems_version: 1.8.5
|
134
117
|
signing_key:
|
135
118
|
specification_version: 3
|
136
119
|
summary: Includes a small library for extracting, parsing and baking animation curves made on Discrodesk Floke/Inflinto, also known as flame
|
137
120
|
test_files:
|
121
|
+
- test/test_channel.rb
|
122
|
+
- test/test_cli.rb
|
138
123
|
- test/test_extractor.rb
|
139
124
|
- test/test_flame_channel_parser.rb
|
140
125
|
- test/test_interpolator.rb
|
126
|
+
- test/test_key.rb
|
141
127
|
- test/test_segments.rb
|
data/lib/parser_2011.rb
DELETED
@@ -1,156 +0,0 @@
|
|
1
|
-
require "delegate"
|
2
|
-
|
3
|
-
# Basic parser used for setups from versions up to 2011
|
4
|
-
class FlameChannelParser::Parser2011
|
5
|
-
|
6
|
-
# Represents a keyframe
|
7
|
-
class Key < Struct.new(:frame, :value, :interpolation, :extrapolation, :left_slope, :right_slope, :break_slope)
|
8
|
-
|
9
|
-
# Unless the key is broken? we should just use the right slope
|
10
|
-
def left_slope
|
11
|
-
return right_slope unless broken?
|
12
|
-
super
|
13
|
-
end
|
14
|
-
|
15
|
-
# Tells whether the slope of this keyframe is broken (not smooth)
|
16
|
-
def broken?
|
17
|
-
break_slope
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
# Defines a number of regular expression matchers applied to the file as it is being parsed
|
22
|
-
def matchers
|
23
|
-
[
|
24
|
-
[:frame, :to_i, /Frame ([\-\d\.]+)/],
|
25
|
-
[:value, :to_f, /Value ([\-\d\.]+)/],
|
26
|
-
[:left_slope, :to_f, /LeftSlope ([\-\d\.]+)/],
|
27
|
-
[:right_slope, :to_f, /RightSlope ([\-\d\.]+)/],
|
28
|
-
[:interpolation, :to_s, /Interpolation (\w+)/],
|
29
|
-
[:extrapolation, :to_s, /Extrapolation (\w+)/],
|
30
|
-
[:break_slope, :to_s, /BreakSlope (\w+)/]
|
31
|
-
]
|
32
|
-
end
|
33
|
-
|
34
|
-
def create_key
|
35
|
-
Key.new
|
36
|
-
end
|
37
|
-
|
38
|
-
# Represents a channel parsed from the Flame setup. Contains
|
39
|
-
# the channel metadata and keyframes
|
40
|
-
class Channel < DelegateClass(Array)
|
41
|
-
attr_reader :node_type
|
42
|
-
attr_reader :node_name
|
43
|
-
attr_accessor :base_value
|
44
|
-
attr_accessor :name
|
45
|
-
attr_accessor :extrapolation
|
46
|
-
|
47
|
-
def initialize(io, channel_name, parent_parser, node_type, node_name)
|
48
|
-
super([])
|
49
|
-
|
50
|
-
@node_type = node_type
|
51
|
-
@node_name = node_name
|
52
|
-
@parser = parent_parser
|
53
|
-
@name = channel_name.strip
|
54
|
-
|
55
|
-
base_value_matcher = /Value ([\-\d\.]+)/
|
56
|
-
keyframe_count_matcher = /Size (\d+)/
|
57
|
-
extrapolation_matcher = /Extrapolation (\w+)/
|
58
|
-
|
59
|
-
indent = nil
|
60
|
-
|
61
|
-
while line = io.gets
|
62
|
-
|
63
|
-
unless indent
|
64
|
-
indent = line.scan(/^(\s+)/)[1]
|
65
|
-
end_mark = "#{indent}End"
|
66
|
-
end
|
67
|
-
|
68
|
-
if line =~ keyframe_count_matcher
|
69
|
-
$1.to_i.times { push(extract_key_from(io)) }
|
70
|
-
elsif line =~ base_value_matcher && empty?
|
71
|
-
self.base_value = $1.to_f
|
72
|
-
elsif line =~ extrapolation_matcher
|
73
|
-
self.extrapolation = symbolize_literal($1)
|
74
|
-
elsif line.strip == end_mark
|
75
|
-
break
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
end
|
80
|
-
|
81
|
-
def path
|
82
|
-
[@node_name, name].compact.join("/")
|
83
|
-
end
|
84
|
-
|
85
|
-
# Get an Interpolator from this channel
|
86
|
-
def to_interpolator
|
87
|
-
FlameChannelParser::Interpolator.new(self)
|
88
|
-
end
|
89
|
-
|
90
|
-
private
|
91
|
-
|
92
|
-
def create_key
|
93
|
-
Key.new
|
94
|
-
end
|
95
|
-
|
96
|
-
INTERPS = [:constant, :linear, :hermite, :natural, :bezier]
|
97
|
-
|
98
|
-
def extract_key_from(io)
|
99
|
-
frame = nil
|
100
|
-
end_matcher = /End/
|
101
|
-
|
102
|
-
key = @parser.create_key
|
103
|
-
matchers = @parser.matchers
|
104
|
-
|
105
|
-
until io.eof?
|
106
|
-
line = io.gets
|
107
|
-
if line =~ end_matcher
|
108
|
-
return key
|
109
|
-
else
|
110
|
-
matchers.each do | property, cast_method, pattern |
|
111
|
-
if line =~ pattern
|
112
|
-
v = symbolize_literal($1.send(cast_method))
|
113
|
-
key.send("#{property}=", v)
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
118
|
-
raise "Did not detect any keyframes!"
|
119
|
-
end
|
120
|
-
|
121
|
-
LITERALS = %w( linear constant natural hermite cubic bezier cycle revcycle )
|
122
|
-
|
123
|
-
def symbolize_literal(v)
|
124
|
-
LITERALS.include?(v) ? v.to_sym : v
|
125
|
-
end
|
126
|
-
|
127
|
-
end
|
128
|
-
|
129
|
-
CHANNEL_MATCHER = /Channel (.+)\n/
|
130
|
-
NODE_TYPE_MATCHER = /Node (\w+)/
|
131
|
-
NODE_NAME_MATCHER = /Name (\w+)/
|
132
|
-
|
133
|
-
def parse(io)
|
134
|
-
channels = []
|
135
|
-
node_name, node_type = nil, nil
|
136
|
-
|
137
|
-
until io.eof?
|
138
|
-
line = io.gets
|
139
|
-
if line =~ NODE_TYPE_MATCHER
|
140
|
-
node_type = $1
|
141
|
-
elsif line =~ NODE_NAME_MATCHER
|
142
|
-
node_name = $1
|
143
|
-
elsif line =~ CHANNEL_MATCHER && channel_is_useful?($1)
|
144
|
-
channels << Channel.new(io, $1, self, node_type, node_name)
|
145
|
-
end
|
146
|
-
end
|
147
|
-
channels
|
148
|
-
end
|
149
|
-
|
150
|
-
# Override this method to skip some channels, this will speedup
|
151
|
-
# your code alot
|
152
|
-
def channel_is_useful?(channel_name)
|
153
|
-
true
|
154
|
-
end
|
155
|
-
|
156
|
-
end
|
data/lib/parser_2012.rb
DELETED
@@ -1,59 +0,0 @@
|
|
1
|
-
require "delegate"
|
2
|
-
require File.dirname(__FILE__) + "/interpolator"
|
3
|
-
|
4
|
-
# This parser is automatically used for 2012 setups
|
5
|
-
class FlameChannelParser::Parser2012 < FlameChannelParser::Parser2011
|
6
|
-
|
7
|
-
class ModernKey < Struct.new(:frame, :value, :r_handle_x, :l_handle_x, :r_handle_y, :l_handle_y, :curve_mode, :curve_order, :break_slope)
|
8
|
-
alias_method :to_s, :inspect
|
9
|
-
|
10
|
-
# Adapter for old interpolation
|
11
|
-
def interpolation
|
12
|
-
return :constant if curve_order.to_s == "constant"
|
13
|
-
return :hermite if curve_order.to_s == "cubic" && (curve_mode.to_s == "hermite" || curve_mode.to_s == "natural")
|
14
|
-
return :bezier if curve_order.to_s == "cubic" && curve_mode.to_s == "bezier"
|
15
|
-
return :linear if curve_order.to_s == "linear"
|
16
|
-
|
17
|
-
raise "Cannot determine interpolation for #{self.inspect}"
|
18
|
-
end
|
19
|
-
|
20
|
-
# Compute pre-212 slope which we use for interpolations
|
21
|
-
def left_slope
|
22
|
-
return right_slope unless broken?
|
23
|
-
|
24
|
-
dy = value - l_handle_y
|
25
|
-
dx = l_handle_x - frame
|
26
|
-
dy / dx * -1
|
27
|
-
end
|
28
|
-
|
29
|
-
# Compute pre-212 slope which we use for interpolations
|
30
|
-
def right_slope
|
31
|
-
dy = value - r_handle_y
|
32
|
-
dx = frame - r_handle_x
|
33
|
-
dy / dx
|
34
|
-
end
|
35
|
-
|
36
|
-
def broken?
|
37
|
-
break_slope
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def matchers
|
42
|
-
[
|
43
|
-
[:frame, :to_i, /Frame ([\-\d\.]+)/],
|
44
|
-
[:value, :to_f, /Value ([\-\d\.]+)/],
|
45
|
-
[:r_handle_x, :to_f, /RHandleX ([\-\d\.]+)/],
|
46
|
-
[:l_handle_x, :to_f, /LHandleX ([\-\d\.]+)/],
|
47
|
-
[:r_handle_y, :to_f, /RHandleY ([\-\d\.]+)/],
|
48
|
-
[:l_handle_y, :to_f, /LHandleY ([\-\d\.]+)/],
|
49
|
-
[:curve_mode, :to_s, /CurveMode (\w+)/],
|
50
|
-
[:curve_order, :to_s, /CurveOrder (\w+)/],
|
51
|
-
[:break_slope, :to_s, /BreakSlope (\w+)/],
|
52
|
-
]
|
53
|
-
end
|
54
|
-
|
55
|
-
|
56
|
-
def create_key
|
57
|
-
ModernKey.new
|
58
|
-
end
|
59
|
-
end
|