[Closed] Mimicing TCB controllers
these work in isolation haven’t been able to solve a “Blend”
fn EaseTo t a =
(
res = t;
if a != 0.0 then
(
a *= 0.02;
k = 1/(2.0 - a);
if t > 1 - a then
res = -k/a * (t * (t - 2)) - (k/a - 1)
else
res = k * (2.0 * t);
)
res;
)
fn EaseFrom t a =
(
res = t;
if a != 0.0 then
(
a *= 0.02;
k = 1/(2.0 - a);
if t <= a then
res = k/a * (t * t)
else
res = k * (2.0 * t) - k*a
)
res;
)
the tcb funtion then becomes
fn tcb timev key0 key1 m0 m1 =
(
local h1, h2, h3, h4;
local t = (timev - key0.time) as float / (key1.time - key0.time) as float;
t = easeFrom t (key0.easeFrom);
--t = easeTo t (key1.easeTo);
hermite t &h1 &h2 &h3 &h4;
h1 * key0.value + h2 * key1.value + h3 * m0 + h4 * m1;
)
I can get it to work if easeIn or easeFrom is set but not both :hmm:
it’s not pretty but it works… after a fashion, going to lie down now :surprised
fn lerp x y s = (x + s * (y - x))
fn cp p = (p * 0.04 - 1.0)
fn EaseFromTo t easefrom easeto =
(
local res = t;
local s = (easefrom + easeto);
if s != 0 then
(
if s > 1.0 then
(
easefrom /= s;
easeto /= s;
)
local k = 1.0/(2.0 - easefrom - easeto);
if t < easefrom then
res = k/easefrom * t * t;
else if t > 1.0 - easeto then
res = 1.0 - k/easeto * (t * (t - 2.0) + 1.0);
else
res = k * (2.0 * t - easefrom);
)
res;
)
-- compute the in tangent
fn GetInTangent k keyframes =
(
key = keyframes[k];
if k == 1 then
prev = keyframes[k]
else
prev = keyframes[k - 1]
if k == keyframes.count then
next = keyframes[k];
else
next = keyframes[k+1];
a = (key.value - prev.value) * (1 - (cp key.tension)) * (1 - (cp key.continuity)) * (1 + (cp key.bias));
b = (next.value - key.value) * (1 - (cp key.tension)) * (1 + (cp key.continuity)) * (1 - (cp key.bias));
t = (key.time - prev.time) as float/(next.time - prev.time) as float;
(b + a) * (lerp t 0.5 (abs (cp key.continuity)));
)
-- compute the out tangent
fn GetOutTangent k keyframes =
(
key = keyframes[k];
if k == 1 then
prev = keyframes[k]
else
prev = keyframes[k - 1]
if k == keyframes.count then
next = keyframes[k];
else
next = keyframes[k+1];
a = (key.value - prev.value) * (1 - (cp key.tension)) * (1 + (cp key.continuity)) * (1 + (cp key.bias));
b = (next.value - key.value) * (1 - (cp key.tension)) * (1 - (cp key.continuity)) * (1 - (cp key.bias));
t = (next.time - key.time) as float/(next.time - prev.time) as float;
(b + a) * (lerp t 0.5 (abs (cp key.continuity)));
)
fn hermite t &h1 &h2 &h3 &h4 =
(
local t2, t3;
t2 = t * t;
t3 = t * t2;
h2 = 3.0 * t2 - t3 - t3;
h1 = 1.0 - h2;
h4 = t3 - t2;
h3 = h4 - t2 + t;
)
fn tcb timev key0 key1 m0 m1 =
(
local h1, h2, h3, h4;
local t = (timev - key0.time) as float / (key1.time - key0.time) as float;
t = EaseFromTo t (key0.easeFrom * 0.02) (key1.easeTo * 0.02);
hermite t &h1 &h2 &h3 &h4;
h1 * key0.value + h2 * key1.value + h3 * m0 + h4 * m1;
)
fn MakeTCBSpline tnode = if tnode != undefined and classof tnode.position.controller == tcb_position do
(
keys = tnode.position.controller.keys;
if keys.count >= 2 then
(
sp = splineshape();
sp.pivot = keys[1].value;
addNewSpline sp;
-- create the first knot
addKnot sp 1 #corner #line keys[1].value
-- loop over the keys
for k = 1 to keys.count - 1 do
(
start_key = k;
end_key = k + 1;
-- create the segment in and out tangents
otan = GetOutTangent start_key keys
itan = GetInTangent end_key keys
-- handle the end conditions
if start_key == 1 then otan = otan * 1.5 - itan * 0.5;
if end_key == keys.count then itan = itan * 1.5 - otan * 0.5;
-- create the knots
for f = keys[start_key].time to keys[end_key].time do
(
if f != keys[start_key].time then
addKnot sp 1 #corner #line (tcb f keys[start_key] keys[end_key] otan itan);
)
)
updateShape sp;
sp.vertexTicks = on
)
)
(
clearlistener()
MakeTCBSpline $
)
Appears to be a cubic-spline or a B-spline curve (according to my MathCAD curve fit).
thanks for the help, found a solution though not b-spline or cubic, linear if x < ease otherwise quadratic.
had to fix a couple of issues with start and end keys when tension, cont and bias != 0
fn lerp x y s = (x + s * (y - x))
fn cp p = (p * 0.04 - 1.0)
fn EaseFromTo t easefrom easeto =
(
local result = t;
local span = (easefrom + easeto);
if span != 0 then
(
if span > 1.0 then
(
easefrom /= span;
easeto /= span;
)
local c = 1.0/(2.0 - easefrom - easeto);
if t < easefrom then
result = c/easefrom * t * t;
else if t > 1.0 - easeto then
result = 1.0 - c/easeto * (t * (t - 2.0) + 1.0);
else
result = c * (2.0 * t - easefrom);
)
result;
)
fn GetInTangent k keyframes =
(
key = keyframes[k];
local continuity = cp key.continuity;
if k == 1 then
prev = keyframes[k]
else
prev = keyframes[k - 1]
if k == keyframes.count then
(
continuity = 0.0;
next = keyframes[k];
)
else
next = keyframes[k+1];
if k == keyframes.count then
(
a = (key.value - prev.value) * (1 - (cp key.tension));
b = (next.value - key.value) * (1 - (cp key.tension));
)
else
(
a = (key.value - prev.value) * (1 - (cp key.tension)) * (1 - continuity) * (1 + (cp key.bias));
b = (next.value - key.value) * (1 - (cp key.tension)) * (1 + continuity) * (1 - (cp key.bias));
)
t = (key.time - prev.time) as float/(next.time - prev.time) as float;
(b + a) * (lerp t 0.5 (abs continuity));
)
fn GetOutTangent k keyframes =
(
key = keyframes[k];
local continuity = cp key.continuity;
if k == 1 then
(
prev = keyframes[k];
continuity = 0.0;
)
else
prev = keyframes[k - 1]
if k == keyframes.count then
next = keyframes[k];
else
next = keyframes[k+1];
if k == 1 then
(
a = (key.value - prev.value) * (1 - (cp key.tension));
b = (next.value - key.value) * (1 - (cp key.tension));
)
else
(
a = (key.value - prev.value) * (1 - (cp key.tension)) * (1 + continuity) * (1 + (cp key.bias));
b = (next.value - key.value) * (1 - (cp key.tension)) * (1 - continuity) * (1 - (cp key.bias));
)
t = (next.time - key.time) as float/(next.time - prev.time) as float;
(b + a) * (lerp t 0.5 (abs continuity));
)
fn tcb timev key0 key1 m0 m1 =
(
local h1, h2, h3, h4, t2, t3;
-- get the segement time in 0-1 range
local t = (timev - key0.time) as float / (key1.time - key0.time) as float;
-- handle the time easefrom and easeto
t = EaseFromTo t (key0.easeFrom * 0.02) (key1.easeTo * 0.02);
print t
-- compute the hermite spline params
t2 = t * t;
t3 = t * t2;
h2 = 3.0 * t2 - t3 - t3;
h1 = 1.0 - h2;
h4 = t3 - t2;
h3 = h4 - t2 + t;
-- interpolate the position
h1 * key0.value + h2 * key1.value + h3 * m0 + h4 * m1;
)
fn MakeTCBSpline tnode = if tnode != undefined and classof tnode.position.controller == tcb_position do
(
keys = tnode.position.controller.keys;
if keys.count >= 2 then
(
sp = splineshape();
sp.pivot = keys[1].value;
addNewSpline sp;
-- create the first knot
addKnot sp 1 #corner #line keys[1].value
-- loop over the keys
for k = 1 to keys.count - 1 do
(
start_key = k;
end_key = k + 1;
-- create the segment in and out tangents
otan = GetOutTangent start_key keys
itan = GetInTangent end_key keys
-- handle the end conditions
if start_key == 1 then otan = otan * 1.5 - itan * (1 - (cp keys[1].tension)) * 0.5;
if end_key == keys.count then itan = itan * 1.5 - otan * (1 - (cp keys[keys.count].tension)) * 0.5;
-- create the knots
for f = keys[start_key].time to keys[end_key].time do
(
if f != keys[start_key].time then
addKnot sp 1 #corner #line (tcb f keys[start_key] keys[end_key] otan itan);
)
)
updateShape sp;
sp.vertexTicks = on
)
)
(
clearlistener()
MakeTCBSpline $
)
would be an interesting challenge to repeat the trick for the Position Bezier Controller.
what do you want to repeat? in-out tangents? i have a post on this forum about how to convert a curvecontrol curve to the bezier float controller
reverse engineer the maths need to generate bezier curve of a bezier position controller based on the key frame data or in other words bezier position controller to spline without using trajectory convert to functionality.
the solution for the bezier controller makes the TCB look like a walk in the park ! a real brain scrambler though very elegant.