flame_channel_parser 1.4.0 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|