flame_channel_parser 1.1.1 → 1.1.2
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/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
|