Notifications
Clear all

[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.

1 Reply
(@denist)
Joined: 2 years ago

Posts: 0

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.

Page 2 / 2