flame_channel_parser 1.1.1 → 1.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest.txt +0 -2
- data/README.rdoc +7 -4
- data/lib/flame_channel_parser.rb +14 -5
- data/lib/interpolator.rb +26 -16
- data/lib/parser_2011.rb +26 -6
- data/lib/parser_2012.rb +3 -0
- data/lib/segments.rb +88 -64
- data/plots.numbers +0 -0
- data/test/test_flame_channel_parser.rb +1 -0
- data/test/test_interpolator.rb +14 -6
- data/test/test_segments.rb +13 -5
- metadata +8 -8
- data/test/baked.csv +0 -201
- data/test/curve.csv +0 -7
data/Manifest.txt
CHANGED
data/README.rdoc
CHANGED
@@ -4,15 +4,18 @@
|
|
4
4
|
|
5
5
|
== DESCRIPTION:
|
6
6
|
|
7
|
-
Includes a small library for parsing and baking
|
7
|
+
Includes a small library for extracting, parsing and baking animation curves made on Discrodesk Floke/Inflinto, also known as flame.
|
8
|
+
Thanks to Marijn Eken, Philippe Soeiro and Andre Gagnon for their support and advice.
|
8
9
|
|
9
10
|
== FEATURES/PROBLEMS:
|
10
11
|
|
11
|
-
*
|
12
|
+
* No pingpong or loop extrapolation for now
|
12
13
|
* Expressions on channels won't be evaluated (obviously!)
|
13
14
|
|
14
15
|
== SYNOPSIS:
|
15
16
|
|
17
|
+
Currently there is no application that you can use directly, the parser is now a library that you can use in your own scripts
|
18
|
+
|
16
19
|
require "flame_channel_parser"
|
17
20
|
channels = File.open("TW_Setup.timewarp") do | f |
|
18
21
|
FlameChannelParser.parse(f)
|
@@ -33,11 +36,11 @@ Includes a small library for parsing and baking anmation curves made on Discrode
|
|
33
36
|
|
34
37
|
== REQUIREMENTS:
|
35
38
|
|
36
|
-
*
|
39
|
+
* Ruby 1.8.6 and above
|
37
40
|
|
38
41
|
== INSTALL:
|
39
42
|
|
40
|
-
*
|
43
|
+
* gem install flame_channel_parser
|
41
44
|
|
42
45
|
== LICENSE:
|
43
46
|
|
data/lib/flame_channel_parser.rb
CHANGED
@@ -1,10 +1,17 @@
|
|
1
1
|
require "delegate"
|
2
2
|
|
3
3
|
module FlameChannelParser
|
4
|
-
VERSION = '1.1.
|
4
|
+
VERSION = '1.1.2'
|
5
5
|
|
6
6
|
# Parse a Flame setup into an array of ChannelBlock objects
|
7
7
|
def self.parse(io)
|
8
|
+
parser_class = detect_parser_class_from(io)
|
9
|
+
parser_class.new.parse(io)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def self.detect_parser_class_from(io)
|
8
15
|
# Scan the IO
|
9
16
|
parser_class = Parser2011
|
10
17
|
until io.eof?
|
@@ -15,10 +22,12 @@ module FlameChannelParser
|
|
15
22
|
end
|
16
23
|
end
|
17
24
|
io.rewind
|
18
|
-
|
25
|
+
|
26
|
+
return parser_class
|
19
27
|
end
|
20
28
|
end
|
21
29
|
|
22
|
-
require File.dirname(__FILE__) + "/parser_2011"
|
23
|
-
require File.dirname(__FILE__) + "/parser_2012"
|
24
|
-
require File.dirname(__FILE__) + "/
|
30
|
+
require File.expand_path(File.dirname(__FILE__)) + "/parser_2011"
|
31
|
+
require File.expand_path(File.dirname(__FILE__)) + "/parser_2012"
|
32
|
+
require File.expand_path(File.dirname(__FILE__)) + "/segments"
|
33
|
+
require File.expand_path(File.dirname(__FILE__)) + "/interpolator"
|
data/lib/interpolator.rb
CHANGED
@@ -6,44 +6,42 @@ require File.expand_path(File.dirname(__FILE__)) + "/segments"
|
|
6
6
|
#
|
7
7
|
# i = Interpolator.new(parsed_channel)
|
8
8
|
# i.value_at(245.5) # => will interpolate and return the value
|
9
|
-
#
|
10
9
|
class FlameChannelParser::Interpolator
|
11
10
|
include FlameChannelParser::Segments
|
12
11
|
|
13
|
-
attr_reader :segments
|
14
|
-
|
15
12
|
NEG_INF = (-1.0/0.0)
|
16
13
|
POS_INF = (1.0/0.0)
|
17
14
|
|
18
15
|
# The constructor will accept a ChannelBlock object and convert it internally to a number of
|
19
16
|
# segments from which samples can be made
|
20
17
|
def initialize(channel)
|
18
|
+
@segments = []
|
21
19
|
|
22
20
|
# Edge case - channel has no anim at all
|
23
21
|
if (channel.length == 0)
|
24
|
-
@segments
|
22
|
+
@segments << [ConstantFunction.new(channel.base_value)]
|
25
23
|
elsif (channel.length == 1)
|
26
|
-
@segments
|
24
|
+
@segments << [ConstantFunction.new(channel[0].value)]
|
27
25
|
else
|
28
|
-
@segments = []
|
29
26
|
|
30
|
-
# TODO: extrapolation is set for the whole channel, both begin and end.
|
31
27
|
# First the prepolating segment
|
32
|
-
@segments <<
|
28
|
+
@segments << pick_prepolation(channel.extrapolation, channel[0])
|
33
29
|
|
34
|
-
#
|
30
|
+
# Then all the intermediate segments, one segment between each pair of keys
|
35
31
|
channel[0..-2].each_with_index do | key, index |
|
36
32
|
@segments << key_pair_to_segment(key, channel[index + 1])
|
37
33
|
end
|
38
34
|
|
39
35
|
# so we just output it separately
|
40
|
-
@segments <<
|
36
|
+
@segments << pick_extrapolation(channel.extrapolation, channel[-1])
|
41
37
|
end
|
42
38
|
end
|
43
39
|
|
44
40
|
# Sample the value of the animation curve at this frame
|
45
41
|
def sample_at(frame)
|
46
42
|
segment = @segments.find{|s| s.defines?(frame) }
|
43
|
+
raise "No segment on this curve that can interpolate the value at #{frame}" unless segment
|
44
|
+
|
47
45
|
segment.value_at(frame)
|
48
46
|
end
|
49
47
|
|
@@ -65,6 +63,23 @@ class FlameChannelParser::Interpolator
|
|
65
63
|
|
66
64
|
private
|
67
65
|
|
66
|
+
def pick_prepolation(extrap_symbol, first_key)
|
67
|
+
if extrap_symbol == :linear
|
68
|
+
LinearPrepolate.new(first_key.frame, first_key.value, first_key.left_slope)
|
69
|
+
else
|
70
|
+
ConstantPrepolate.new(first_key.frame, first_key.value)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def pick_extrapolation(extrap_symbol, last_key)
|
75
|
+
if extrap_symbol == :linear
|
76
|
+
LinearExtrapolate.new(last_key.frame, last_key.value, last_key.right_slope)
|
77
|
+
else
|
78
|
+
ConstantExtrapolate.new(last_key.frame, last_key.value)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
|
68
83
|
# We need both the preceding and the next key
|
69
84
|
def key_pair_to_segment(key, next_key)
|
70
85
|
case key.interpolation
|
@@ -75,7 +90,7 @@ class FlameChannelParser::Interpolator
|
|
75
90
|
key.r_handle_y,
|
76
91
|
next_key.l_handle_x, next_key.l_handle_y)
|
77
92
|
when :natural, :hermite
|
78
|
-
HermiteSegment.new(key.frame, next_key.frame, key.value, next_key.value, key.right_slope,
|
93
|
+
HermiteSegment.new(key.frame, next_key.frame, key.value, next_key.value, key.right_slope, next_key.left_slope)
|
79
94
|
when :constant
|
80
95
|
ConstantSegment.new(key.frame, next_key.frame, key.value)
|
81
96
|
else # Linear and safe
|
@@ -83,10 +98,5 @@ class FlameChannelParser::Interpolator
|
|
83
98
|
end
|
84
99
|
end
|
85
100
|
|
86
|
-
# Flame uses the right slope for both left and right unless the BrokenSlope tag is set
|
87
|
-
def incoming_slope(key)
|
88
|
-
key.broken? ? key.left_slope : key.right_slope
|
89
|
-
end
|
90
|
-
|
91
101
|
end
|
92
102
|
|
data/lib/parser_2011.rb
CHANGED
@@ -1,16 +1,26 @@
|
|
1
1
|
require "delegate"
|
2
2
|
|
3
|
+
# Basic parser used for setups from versions up to 2011
|
3
4
|
class FlameChannelParser::Parser2011
|
4
5
|
|
6
|
+
# Represents a keyframe
|
5
7
|
class Key
|
6
8
|
attr_accessor :frame, :value, :interpolation, :extrapolation, :left_slope, :right_slope, :break_slope
|
7
9
|
alias_method :to_s, :inspect
|
8
10
|
|
11
|
+
# Unless the key is broken? we should just use the right slope
|
12
|
+
def left_slope
|
13
|
+
return right_slope unless broken?
|
14
|
+
@left_slope
|
15
|
+
end
|
16
|
+
|
17
|
+
# Tells whether the slope of this keyframe is broken (not smooth)
|
9
18
|
def broken?
|
10
19
|
break_slope
|
11
20
|
end
|
12
21
|
end
|
13
22
|
|
23
|
+
# Defines a number of regular expression matchers applied to the file as it is being parsed
|
14
24
|
def matchers
|
15
25
|
[
|
16
26
|
[:frame, :to_i, /Frame ([\-\d\.]+)/],
|
@@ -27,9 +37,13 @@ class FlameChannelParser::Parser2011
|
|
27
37
|
Key.new
|
28
38
|
end
|
29
39
|
|
40
|
+
# Represents a channel parsed from the Flame setup. Contains
|
41
|
+
# the channel metadata and keyframes
|
30
42
|
class ChannelBlock < DelegateClass(Array)
|
31
43
|
attr_accessor :base_value
|
32
44
|
attr_accessor :name
|
45
|
+
attr_accessor :extrapolation
|
46
|
+
|
33
47
|
def initialize(io, channel_name, parent_parser)
|
34
48
|
super([])
|
35
49
|
|
@@ -38,6 +52,7 @@ class FlameChannelParser::Parser2011
|
|
38
52
|
|
39
53
|
base_value_matcher = /Value ([\-\d\.]+)/
|
40
54
|
keyframe_count_matcher = /Size (\d+)/
|
55
|
+
extrapolation_matcher = /Extrapolation (\w+)/
|
41
56
|
indent = nil
|
42
57
|
|
43
58
|
while line = io.gets
|
@@ -51,6 +66,8 @@ class FlameChannelParser::Parser2011
|
|
51
66
|
$1.to_i.times { push(extract_key_from(io)) }
|
52
67
|
elsif line =~ base_value_matcher && empty?
|
53
68
|
self.base_value = $1.to_f
|
69
|
+
elsif line =~ extrapolation_matcher
|
70
|
+
self.extrapolation = symbolize_literal($1)
|
54
71
|
elsif line.strip == end_mark
|
55
72
|
break
|
56
73
|
end
|
@@ -58,7 +75,12 @@ class FlameChannelParser::Parser2011
|
|
58
75
|
|
59
76
|
end
|
60
77
|
|
61
|
-
|
78
|
+
# Get an Interpolator from this channel
|
79
|
+
def to_interpolator
|
80
|
+
FlameChannelParser::Inteprolator.new(self)
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
62
84
|
|
63
85
|
def create_key
|
64
86
|
Key.new
|
@@ -89,7 +111,7 @@ class FlameChannelParser::Parser2011
|
|
89
111
|
raise "Did not detect any keyframes!"
|
90
112
|
end
|
91
113
|
|
92
|
-
LITERALS = %w( linear constant natural hermite)
|
114
|
+
LITERALS = %w( linear constant natural hermite cubic bezier)
|
93
115
|
|
94
116
|
def symbolize_literal(v)
|
95
117
|
LITERALS.include?(v) ? v.to_sym : v
|
@@ -104,18 +126,16 @@ class FlameChannelParser::Parser2011
|
|
104
126
|
until io.eof?
|
105
127
|
line = io.gets
|
106
128
|
if line =~ CHANNEL_MATCHER && channel_is_useful?($1)
|
107
|
-
report_progress("Extracting channel #{$1}")
|
108
129
|
channels << ChannelBlock.new(io, $1, self)
|
109
130
|
end
|
110
131
|
end
|
111
132
|
channels
|
112
133
|
end
|
113
134
|
|
135
|
+
# Override this method to skip some channels, this will speedup
|
136
|
+
# your code alot
|
114
137
|
def channel_is_useful?(channel_name)
|
115
138
|
true
|
116
139
|
end
|
117
140
|
|
118
|
-
def report_progress(message)
|
119
|
-
# flunk
|
120
|
-
end
|
121
141
|
end
|
data/lib/parser_2012.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require "delegate"
|
2
2
|
require File.dirname(__FILE__) + "/interpolator"
|
3
3
|
|
4
|
+
# This parser is automatically used for 2012 setups
|
4
5
|
class FlameChannelParser::Parser2012 < FlameChannelParser::Parser2011
|
5
6
|
|
6
7
|
class ModernKey
|
@@ -19,6 +20,8 @@ class FlameChannelParser::Parser2012 < FlameChannelParser::Parser2011
|
|
19
20
|
|
20
21
|
# Compute pre-212 slope which we use for interpolations
|
21
22
|
def left_slope
|
23
|
+
return right_slope unless broken?
|
24
|
+
|
22
25
|
dy = value - l_handle_y
|
23
26
|
dx = l_handle_x - frame
|
24
27
|
dy / dx * -1
|
data/lib/segments.rb
CHANGED
@@ -23,7 +23,6 @@ module FlameChannelParser::Segments
|
|
23
23
|
def initialize(from_frame, to_frame, value)
|
24
24
|
@start_frame = from_frame
|
25
25
|
@end_frame = to_frame
|
26
|
-
|
27
26
|
@v1 = value
|
28
27
|
end
|
29
28
|
end
|
@@ -65,19 +64,22 @@ module FlameChannelParser::Segments
|
|
65
64
|
# CC = {P1, P2, T1, T2}
|
66
65
|
p1, p2, t1, t2 = value1, value2, tangent1.to_f * frame_interval, tangent2.to_f * frame_interval
|
67
66
|
@hermite = Vector[p1, p2, t1, t2]
|
67
|
+
@basis = HERMATRIX * @hermite
|
68
68
|
end
|
69
69
|
|
70
70
|
# P[s_] = S[s].h.CC where s is 0..1 float interpolant on T (interval)
|
71
71
|
def value_at(frame)
|
72
|
-
|
72
|
+
return @hermite[0] if frame == @start_frame
|
73
|
+
|
74
|
+
# Get the 0 < T < 1 interval we will interpolate on
|
73
75
|
# Q[frame_] = P[ ( frame - 149 ) / (time_to - time_from)]
|
74
|
-
|
76
|
+
t = (frame - @start_frame).to_f / (@end_frame - @start_frame)
|
75
77
|
|
76
78
|
# S[s_] = {s^3, s^2, s^1, s^0}
|
77
|
-
multipliers_vec = Vector[
|
79
|
+
multipliers_vec = Vector[t ** 3, t ** 2, t ** 1, t ** 0]
|
78
80
|
|
79
81
|
# P[s_] = S[s].h.CC --> Kaboom!
|
80
|
-
interpolated_scalar = dot_product(
|
82
|
+
interpolated_scalar = dot_product(@basis, multipliers_vec)
|
81
83
|
end
|
82
84
|
|
83
85
|
private
|
@@ -95,17 +97,19 @@ module FlameChannelParser::Segments
|
|
95
97
|
class BezierSegment < LinearSegment
|
96
98
|
def initialize(x1, x2, y1, y2, t1x, t1y, t2x, t2y)
|
97
99
|
@start_frame, @end_frame = x1, x2
|
98
|
-
|
100
|
+
|
99
101
|
@a = Point.new(x1, y1, t1x, t1y)
|
100
102
|
@b = Point.new(x2, y2, t2x, t2y)
|
101
103
|
end
|
102
|
-
|
104
|
+
|
103
105
|
def value_at(frame)
|
106
|
+
return @a.y if frame == @start_frame
|
107
|
+
|
104
108
|
# Solve T from X. This determines the correlation between X and T.
|
105
109
|
t = approximate_t(frame, @a.x, @a.tanx, @b.tanx, @b.x)
|
106
110
|
vy = bezier(t, @a.y, @a.tany, @b.tany, @b.y)
|
107
111
|
end
|
108
|
-
|
112
|
+
|
109
113
|
private
|
110
114
|
|
111
115
|
# t is the T interpolant (0 < T < 1)
|
@@ -116,68 +120,65 @@ module FlameChannelParser::Segments
|
|
116
120
|
def bezier(t, a, b, c, d)
|
117
121
|
a + (a*(-3) + b*3)*(t) + (a*3 - b*6 + c*3)*(t**2) + (-a + b*3 - c*3 + d)*(t**3)
|
118
122
|
end
|
119
|
-
|
123
|
+
|
120
124
|
def clamp(value)
|
121
125
|
return 0.0 if value < 0
|
122
126
|
return 1.0 if value > 1
|
123
127
|
return value
|
124
128
|
end
|
125
|
-
|
126
|
-
|
127
|
-
# * Returns the approximated parameter of a parametric curve for the value X
|
128
|
-
# * @param atX At which value should the parameter be evaluated
|
129
|
-
# * @param p0x The first interpolation point of a curve segment
|
130
|
-
# * @param c0x The first control point of a curve segment
|
131
|
-
# * @param c1x The second control point of a curve segment
|
132
|
-
# * @param P1_x The second interpolation point of a curve segment
|
133
|
-
# * @return The parametric argument that is used to retrieve atX using the parametric function representation of this curve
|
134
|
-
# */
|
135
|
-
|
129
|
+
|
130
|
+
|
136
131
|
APPROXIMATION_EPSILON = 1.0e-09
|
137
132
|
VERYSMALL = 1.0e-20
|
138
|
-
MAXIMUM_ITERATIONS =
|
139
|
-
|
133
|
+
MAXIMUM_ITERATIONS = 100
|
134
|
+
|
140
135
|
# This is how OPENCOLLADA suggests approximating Bezier animation curves
|
141
136
|
# http://www.collada.org/public_forum/viewtopic.php?f=12&t=1132
|
137
|
+
# Returns the approximated parameter of a parametric curve for the value X
|
138
|
+
# @param atX At which value should the parameter be evaluated
|
139
|
+
# @param p0x The first interpolation point of a curve segment
|
140
|
+
# @param c0x The first control point of a curve segment
|
141
|
+
# @param c1x The second control point of a curve segment
|
142
|
+
# @param P1_x The second interpolation point of a curve segment
|
143
|
+
# @return The parametric argument that is used to retrieve atX using the parametric function representation of this curve
|
142
144
|
def approximate_t (atX, p0x, c0x, c1x, p1x )
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
145
|
+
|
146
|
+
return 0.0 if (atX - p0x < VERYSMALL)
|
147
|
+
return 1.0 if (p1x - atX < VERYSMALL)
|
148
|
+
|
149
|
+
u, v = 0.0, 1.0
|
150
|
+
|
151
|
+
# iteratively apply subdivision to approach value atX
|
152
|
+
MAXIMUM_ITERATIONS.times do
|
153
|
+
|
154
|
+
# de Casteljau Subdivision.
|
155
|
+
a = (p0x + c0x) / 2.0
|
156
|
+
b = (c0x + c1x) / 2.0
|
157
|
+
c = (c1x + p1x) / 2.0
|
158
|
+
d = (a + b) / 2.0
|
159
|
+
e = (b + c) / 2.0
|
160
|
+
f = (d + e) / 2.0 # this one is on the curve!
|
161
|
+
|
162
|
+
# The curve point is close enough to our wanted atX
|
163
|
+
if ((f - atX).abs < APPROXIMATION_EPSILON)
|
164
|
+
return clamp((u + v)*0.5)
|
165
|
+
end
|
166
|
+
|
167
|
+
# dichotomy
|
168
|
+
if (f < atX)
|
169
|
+
p0x = f
|
170
|
+
c0x = e
|
171
|
+
c1x = c
|
172
|
+
u = (u + v) / 2.0
|
173
|
+
else
|
174
|
+
c0x = a
|
175
|
+
c1x = d
|
176
|
+
p1x = f
|
177
|
+
v = (u + v) / 2.0
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
clamp((u + v) / 2.0)
|
181
182
|
end
|
182
183
|
end
|
183
184
|
|
@@ -192,11 +193,21 @@ module FlameChannelParser::Segments
|
|
192
193
|
def value_at(frame)
|
193
194
|
@value
|
194
195
|
end
|
196
|
+
end
|
195
197
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
198
|
+
# This segment does prepolation with a linear coefficient
|
199
|
+
class LinearPrepolate < LinearSegment
|
200
|
+
def initialize(upto_frame, base_value, tangent)
|
201
|
+
@value = base_value
|
202
|
+
@end_frame = upto_frame
|
203
|
+
@start_frame = NEG_INF
|
204
|
+
@tangent = tangent.to_f
|
205
|
+
end
|
206
|
+
|
207
|
+
def value_at(frame)
|
208
|
+
frame_diff = (frame - @end_frame)
|
209
|
+
@value + (@tangent * frame_diff)
|
210
|
+
end
|
200
211
|
end
|
201
212
|
|
202
213
|
# This segment does extrapolation using a constant value
|
@@ -212,6 +223,19 @@ module FlameChannelParser::Segments
|
|
212
223
|
end
|
213
224
|
end
|
214
225
|
|
226
|
+
# This segment does extrapolation using the tangent from the preceding keyframe
|
227
|
+
class LinearExtrapolate < ConstantExtrapolate
|
228
|
+
def initialize(from_frame, base_value, tangent)
|
229
|
+
super(from_frame, base_value)
|
230
|
+
@tangent = tangent
|
231
|
+
end
|
232
|
+
|
233
|
+
def value_at(frame)
|
234
|
+
frame_diff = (frame - @start_frame)
|
235
|
+
@base_value + (@tangent * frame_diff)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
215
239
|
# This can be used for an anim curve that stays constant all along
|
216
240
|
class ConstantFunction < ConstantSegment
|
217
241
|
|
data/plots.numbers
CHANGED
Binary file
|
data/test/test_interpolator.rb
CHANGED
@@ -6,6 +6,16 @@ require File.dirname(__FILE__) + "/../lib/flame_channel_parser"
|
|
6
6
|
class TestInterpolator < Test::Unit::TestCase
|
7
7
|
DELTA = 0.05
|
8
8
|
|
9
|
+
def send_curves_to_clipboard(range, ref_i, sample_i)
|
10
|
+
# This is handy for plotting
|
11
|
+
begin
|
12
|
+
IO.popen("pbcopy", "w") do | buf |
|
13
|
+
range.map{|f| buf.puts "%03f\t%03f" % [ref_i.sample_at(f), sample_i.sample_at(f)] }
|
14
|
+
end
|
15
|
+
rescue Errno::EPIPE # There is no pbcopy on this box, sorry
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
9
19
|
def assert_same_interpolation(range, ref_channel, sample_channel)
|
10
20
|
ref_i, sample_i = [ref_channel, sample_channel].map{|c| FlameChannelParser::Interpolator.new(c) }
|
11
21
|
|
@@ -19,10 +29,7 @@ class TestInterpolator < Test::Unit::TestCase
|
|
19
29
|
end
|
20
30
|
rescue Test::Unit::AssertionFailedError => e
|
21
31
|
STDERR.puts "Curves were not the same so I will now copy the two curves to the clipboard"
|
22
|
-
|
23
|
-
IO.popen("pbcopy", "w") do | buf |
|
24
|
-
range.map{|f| buf.puts "%03f\t%03f" % [ref_i.sample_at(f), sample_i.sample_at(f)] }
|
25
|
-
end
|
32
|
+
send_curves_to_clipboard(range, ref_i, sample_i)
|
26
33
|
raise e
|
27
34
|
end
|
28
35
|
end
|
@@ -32,7 +39,8 @@ class TestInterpolator < Test::Unit::TestCase
|
|
32
39
|
constants = FlameChannelParser.parse(data).find{|c| c.name == "constants"}
|
33
40
|
interp = FlameChannelParser::Interpolator.new(constants)
|
34
41
|
|
35
|
-
vs = [770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41,
|
42
|
+
vs = [770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41,
|
43
|
+
770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 770.41, 858.177, 858.177, 858.177, 858.177, 858.177, 858.177, 858.177, 858.177, 858.177, 858.177, 858.177, 858.177, 858.177, 858.177, 858.177, 858.177, 858.177, 858.177, 858.177, 858.177, 858.177, 858.177, 858.177, 858.177, 858.177, 858.177, 858.177, 858.177, 858.177, 858.177, 939.407, 939.407, 939.407, 939.407, 939.407, 939.407, 939.407, 939.407, 939.407, 939.407, 939.407, 939.407, 939.407, 939.407, 939.407, 939.407, 939.407, 939.407, 939.407, 939.407, 939.407, 939.407, 939.407, 939.407, 939.407, 939.407, 939.407, 939.407, 939.407, 939.407, 939.407, 939.407, 939.407, 939.407, 939.407, 939.407, 939.407, 939.407, 939.407, 939.407, 939.407, 1017.36, 1017.36]
|
36
44
|
|
37
45
|
values = (-5..116).map{|f| interp.sample_at(f) }
|
38
46
|
assert_equal vs, values
|
@@ -84,6 +92,6 @@ class TestInterpolator < Test::Unit::TestCase
|
|
84
92
|
|
85
93
|
reference = channels_in_action.find{|c| c.name == "position/x" && c.length > 2 }
|
86
94
|
sampled = channels_in_action.find{|c| c.name == "position/y" && c.length > 2 }
|
87
|
-
assert_same_interpolation(
|
95
|
+
assert_same_interpolation(-10..300, reference, sampled)
|
88
96
|
end
|
89
97
|
end
|
data/test/test_segments.rb
CHANGED
@@ -31,7 +31,6 @@ class TestConstantSegment < Test::Unit::TestCase
|
|
31
31
|
end
|
32
32
|
|
33
33
|
class TestBezierSegment < Test::Unit::TestCase
|
34
|
-
D = 0.001
|
35
34
|
|
36
35
|
def test_segment
|
37
36
|
seg = BezierSegment.new(
|
@@ -248,14 +247,23 @@ class TestHermiteSegment < Test::Unit::TestCase
|
|
248
247
|
end
|
249
248
|
|
250
249
|
class TestLinearPrepolate < Test::Unit::TestCase
|
251
|
-
def
|
252
|
-
|
250
|
+
def test_segment
|
251
|
+
seg = LinearPrepolate.new(123, -4, 2)
|
252
|
+
assert seg.defines?(122)
|
253
|
+
assert seg.defines?(-99999)
|
254
|
+
assert_equal( -2, seg.value_at(124))
|
255
|
+
assert_equal( -52, seg.value_at(99))
|
253
256
|
end
|
254
257
|
end
|
255
258
|
|
256
259
|
class TestLinearExtrapolate < Test::Unit::TestCase
|
257
|
-
def
|
258
|
-
|
260
|
+
def test_segment
|
261
|
+
seg = LinearExtrapolate.new(123, -4, 2)
|
262
|
+
assert seg.defines?(123)
|
263
|
+
assert seg.defines?(9999999999)
|
264
|
+
assert !seg.defines?(122)
|
265
|
+
assert_equal( -2, seg.value_at(124))
|
266
|
+
assert_equal 198, seg.value_at(224)
|
259
267
|
end
|
260
268
|
end
|
261
269
|
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flame_channel_parser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 1.1.
|
9
|
+
- 2
|
10
|
+
version: 1.1.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Julik Tarkhanov
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-05-
|
18
|
+
date: 2011-05-20 00:00:00 +02:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -34,7 +34,9 @@ dependencies:
|
|
34
34
|
version: 2.9.1
|
35
35
|
type: :development
|
36
36
|
version_requirements: *id001
|
37
|
-
description:
|
37
|
+
description: |-
|
38
|
+
Includes a small library for extracting, parsing and baking animation curves made on Discrodesk Floke/Inflinto, also known as flame.
|
39
|
+
Thanks to Marijn Eken, Philippe Soeiro and Andre Gagnon for their support and advice.
|
38
40
|
email:
|
39
41
|
- me@julik.nl
|
40
42
|
executables: []
|
@@ -58,9 +60,7 @@ files:
|
|
58
60
|
- lib/segments.rb
|
59
61
|
- plots.numbers
|
60
62
|
- plots_2012.numbers
|
61
|
-
- test/baked.csv
|
62
63
|
- test/channel_with_constants.dat
|
63
|
-
- test/curve.csv
|
64
64
|
- test/sample_channel.dat
|
65
65
|
- test/snaps/FLEM_BrokenTangents.action
|
66
66
|
- test/snaps/FLEM_advanced_curve.png
|
@@ -108,7 +108,7 @@ rubyforge_project: flame_channel_parser
|
|
108
108
|
rubygems_version: 1.4.1
|
109
109
|
signing_key:
|
110
110
|
specification_version: 3
|
111
|
-
summary: Includes a small library for parsing and baking
|
111
|
+
summary: Includes a small library for extracting, parsing and baking animation curves made on Discrodesk Floke/Inflinto, also known as flame
|
112
112
|
test_files:
|
113
113
|
- test/test_flame_channel_parser.rb
|
114
114
|
- test/test_interpolator.rb
|
data/test/baked.csv
DELETED
@@ -1,201 +0,0 @@
|
|
1
|
-
x,y
|
2
|
-
1,-459.0
|
3
|
-
2,-426.528
|
4
|
-
3,-394.055
|
5
|
-
4,-361.583
|
6
|
-
5,-329.111
|
7
|
-
6,-296.638
|
8
|
-
7,-264.166
|
9
|
-
8,-231.694
|
10
|
-
9,-199.221
|
11
|
-
10,-166.749
|
12
|
-
11,-134.277
|
13
|
-
12,-101.804
|
14
|
-
13,-69.3319
|
15
|
-
14,-36.8595
|
16
|
-
15,-4.38721
|
17
|
-
16,28.0851
|
18
|
-
17,60.5575
|
19
|
-
18,93.0298
|
20
|
-
19,125.502
|
21
|
-
20,157.975
|
22
|
-
21,190.447
|
23
|
-
22,222.919
|
24
|
-
23,222.543
|
25
|
-
24,221.735
|
26
|
-
25,220.506
|
27
|
-
26,218.867
|
28
|
-
27,216.83
|
29
|
-
28,214.406
|
30
|
-
29,211.608
|
31
|
-
30,208.445
|
32
|
-
31,204.931
|
33
|
-
32,201.075
|
34
|
-
33,196.89
|
35
|
-
34,192.387
|
36
|
-
35,187.578
|
37
|
-
36,182.473
|
38
|
-
37,177.085
|
39
|
-
38,171.425
|
40
|
-
39,165.503
|
41
|
-
40,159.333
|
42
|
-
41,152.924
|
43
|
-
42,146.29
|
44
|
-
43,139.44
|
45
|
-
44,132.386
|
46
|
-
45,125.141
|
47
|
-
46,117.715
|
48
|
-
47,110.119
|
49
|
-
48,102.366
|
50
|
-
49,94.4661
|
51
|
-
50,86.4316
|
52
|
-
51,78.2735
|
53
|
-
52,70.0034
|
54
|
-
53,61.6327
|
55
|
-
54,53.1728
|
56
|
-
55,44.6353
|
57
|
-
56,36.0314
|
58
|
-
57,27.3727
|
59
|
-
58,18.6705
|
60
|
-
59,9.93645
|
61
|
-
60,1.18185
|
62
|
-
61,-7.58185
|
63
|
-
62,-16.3432
|
64
|
-
63,-25.0907
|
65
|
-
64,-33.813
|
66
|
-
65,-42.4985
|
67
|
-
66,-51.136
|
68
|
-
67,-59.714
|
69
|
-
68,-68.2209
|
70
|
-
69,-76.6454
|
71
|
-
70,-84.9759
|
72
|
-
71,-93.2012
|
73
|
-
72,-101.31
|
74
|
-
73,-109.29
|
75
|
-
74,-117.131
|
76
|
-
75,-124.82
|
77
|
-
76,-132.347
|
78
|
-
77,-139.7
|
79
|
-
78,-146.868
|
80
|
-
79,-153.839
|
81
|
-
80,-160.601
|
82
|
-
81,-167.144
|
83
|
-
82,-173.456
|
84
|
-
83,-179.525
|
85
|
-
84,-185.34
|
86
|
-
85,-190.889
|
87
|
-
86,-196.162
|
88
|
-
87,-201.146
|
89
|
-
88,-205.83
|
90
|
-
89,-210.204
|
91
|
-
90,-214.254
|
92
|
-
91,-217.97
|
93
|
-
92,-221.341
|
94
|
-
93,-224.355
|
95
|
-
94,-227.0
|
96
|
-
95,-227.0
|
97
|
-
96,-227.0
|
98
|
-
97,-227.0
|
99
|
-
98,-227.0
|
100
|
-
99,-227.0
|
101
|
-
100,-227.0
|
102
|
-
101,-227.0
|
103
|
-
102,-227.0
|
104
|
-
103,-227.0
|
105
|
-
104,-227.0
|
106
|
-
105,-227.0
|
107
|
-
106,-227.0
|
108
|
-
107,-227.0
|
109
|
-
108,-227.0
|
110
|
-
109,-227.0
|
111
|
-
110,-227.0
|
112
|
-
111,-227.0
|
113
|
-
112,-227.0
|
114
|
-
113,-227.0
|
115
|
-
114,-227.0
|
116
|
-
115,-227.0
|
117
|
-
116,-227.0
|
118
|
-
117,1.23907
|
119
|
-
118,15.1943
|
120
|
-
119,28.8842
|
121
|
-
120,42.297
|
122
|
-
121,55.421
|
123
|
-
122,68.2446
|
124
|
-
123,80.7561
|
125
|
-
124,92.9438
|
126
|
-
125,104.796
|
127
|
-
126,116.301
|
128
|
-
127,127.447
|
129
|
-
128,138.223
|
130
|
-
129,148.616
|
131
|
-
130,158.616
|
132
|
-
131,168.21
|
133
|
-
132,177.387
|
134
|
-
133,186.135
|
135
|
-
134,194.442
|
136
|
-
135,202.297
|
137
|
-
136,209.689
|
138
|
-
137,216.604
|
139
|
-
138,223.032
|
140
|
-
139,228.961
|
141
|
-
140,234.38
|
142
|
-
141,239.276
|
143
|
-
142,243.638
|
144
|
-
143,247.455
|
145
|
-
144,250.714
|
146
|
-
145,253.404
|
147
|
-
146,255.513
|
148
|
-
147,257.03
|
149
|
-
148,257.942
|
150
|
-
149,258.239
|
151
|
-
150,257.937
|
152
|
-
151,257.074
|
153
|
-
152,255.674
|
154
|
-
153,253.759
|
155
|
-
154,251.352
|
156
|
-
155,248.476
|
157
|
-
156,245.152
|
158
|
-
157,241.405
|
159
|
-
158,237.255
|
160
|
-
159,232.727
|
161
|
-
160,227.842
|
162
|
-
161,222.624
|
163
|
-
162,217.094
|
164
|
-
163,211.276
|
165
|
-
164,205.192
|
166
|
-
165,198.865
|
167
|
-
166,192.317
|
168
|
-
167,185.571
|
169
|
-
168,178.65
|
170
|
-
169,171.576
|
171
|
-
170,164.372
|
172
|
-
171,157.06
|
173
|
-
172,149.664
|
174
|
-
173,142.205
|
175
|
-
174,134.707
|
176
|
-
175,127.192
|
177
|
-
176,119.683
|
178
|
-
177,112.202
|
179
|
-
178,104.772
|
180
|
-
179,97.4151
|
181
|
-
180,90.1547
|
182
|
-
181,83.0132
|
183
|
-
182,76.0132
|
184
|
-
183,69.1772
|
185
|
-
184,62.528
|
186
|
-
185,56.0881
|
187
|
-
186,49.8802
|
188
|
-
187,43.9269
|
189
|
-
188,38.2508
|
190
|
-
189,32.8746
|
191
|
-
190,27.8209
|
192
|
-
191,23.1124
|
193
|
-
192,18.7715
|
194
|
-
193,14.8212
|
195
|
-
194,11.2838
|
196
|
-
195,8.18198
|
197
|
-
196,5.53853
|
198
|
-
197,3.37598
|
199
|
-
198,1.71692
|
200
|
-
199,0.584045
|
201
|
-
200,0.0
|
data/test/curve.csv
DELETED
@@ -1,7 +0,0 @@
|
|
1
|
-
frame,value,interpolation,extrapolation,left_slope,right_slope
|
2
|
-
1,-459.0,linear,,,
|
3
|
-
22,222.919,hermite,,2.49462,-0.156017
|
4
|
-
94,-227.0,constant,,-2.33348,-2.45723
|
5
|
-
117,1.23907,natural,,14.0841,14.0841
|
6
|
-
149,258.239,hermite,,-0.0149286,-0.0149286
|
7
|
-
200,0.0,hermite,,,-0.302127
|