Activity › Forums › Adobe After Effects Expressions › OVERSHOOT expression only based on Velocity – NOT keyframes
-
OVERSHOOT expression only based on Velocity – NOT keyframes
Posted by Remi Monedi on February 27, 2020 at 7:05 pmHi everyone,
I’m working on a project where I have a Null “A” moving with expressions. I have another Null “B” with its position linked to “A”. I’d like the position of “B” to “overshoot“.
We see many expressions around using the famous overshoot expression from Dan Ebberts but they are ALL using a keyframed animation as reference at some point.
What if, like in my situation, you don’t have any keyframes! Could it be possible to make an overshoot expression using only the velocity (for instance) of a property? Is this even possible?Have a great day
Dan Ebberts replied 2 weeks, 3 days ago 3 Members · 10 Replies -
10 Replies
-
Dan Ebberts
February 27, 2020 at 7:40 pmYou could, but the expression would have to know when to pick up the velocity. What determines when Null A stops its motion?
Dan
-
Remi Monedi
February 27, 2020 at 9:39 pmNothing in particular since I was thinking of the most versatile expression (the position of “A”could be set with expressions, another layer, keyframes). This is why I thought of its velocity as the main reference for its motion.
The expression you’re talking about Dan, it can’t watch for the velocity during the whole composition?
-
Dan Ebberts
February 27, 2020 at 9:49 pmWhat you want is the velocity when the force is removed. For example, with this expression:
easeIn(time,0,1,[0,0],[500,500]);
You’d want to sample the velocity at 0.999 sec (right before the layer stops moving).
Dan
-
Remi Monedi
February 28, 2020 at 11:36 amTo calculate the velocity I found several methods, I tried them all and got to those conclusions :
The difference : property-property.valueAtTime(time-.01)
The velocityAtTime(), but I couldn’t get it to work with a property, maybe this is my mistake.
The .speed function, simple enough, I chose this one.Then I tried to tweak your overshoot expression Dan, this time, with a property of only 1 array, the Rotation of Null “A” (to make things easier).
Here is the expression of the Rotation of Null “A” :
linear(time,0,0.1,0,180);And the expression of the rotation of Nul “B” :
// —– REFERENCE PROPERTY —–
ref = thisComp.layer(“A”).transform.rotation;
refvelocity = ref.speed// —– OVERSHOOT —–
freq = 3;
decay = 5;t = time – inPoint;
dur = 0.1;
if (refvelocity != 0){
ref
}else{
amp = 180/dur;
w = freq*Math.PI*2;
ref + amp*(Math.sin((t-dur)*w)/Math.exp(decay*(t-dur))/w);
}It works but I have 2 problems to fully “automate” this expression, mainly because when it comes to time in expression, I’m very quickly lost :
1. the duration of the animation “dur” is a value, but it could be :
dur = Moment when “refvelocity” change its value back to 0 (end of a movement) – Moment when “refvelocity” change its value to something different than 0 (start of a movement)
2. the amplitude “amp” is a value too but it could be
amp = Value of “ref” when “refvelocity” change its value back to 0 – Value of “ref” when “refvelocity” change its value to something different than 0
Is it possible to get those informations?
If yes, which expression should I use, or how you should I write it? -
Remi Monedi
February 28, 2020 at 1:17 pmI found this expression used in Duik from Rainboxprod : instead of using keyframes it uses the anchorpoint Position of the target layer which is parented to another one to overshoot its position.BUT it only works for Position and Anchorpoint.
I tried to modify this expression to fit my needs :
Instead of having a target layer “B” parented to another layer “A” and the position of “B” being “overshot” by the movement of “A”, I want to have any property (position, scale, rotation, …) of “B” linked to the same properties of “A” and being overshot as the properties of “A” change.
But without success…I think the key is somewhere in these lines of code :
worldVelocityX = (thisLayer.toWorld(thisLayer.anchorPoint,TIME)[0]-thisLayer.toWorld(thisLayer.anchorPoint,TIME-.01)[0])*100;
worldVelocityY = (thisLayer.toWorld(thisLayer.anchorPoint,TIME)[1]-thisLayer.toWorld(thisLayer.anchorPoint,TIME-.01)[1])*100;But I cannot make it work with other properties than anchorpoint. Maybe this is because of my misunderstanding of Speed VS Velocity and how it applies in the world of expression.
Could this expression be adapted to fit my needs, or should I go with the first one I was trying to adapt?
decay = 5;
freq = 3;
if (decay == 0) decay = 0.1;
if (freq == 0) freq = 0.1;
delay = freq/decay;
weight = 1/decay/10;
frameDur = thisComp.frameDuration;
function worldVelocity(TIME) {
worldVelocityX = (thisLayer.toWorld(thisLayer.anchorPoint,TIME)[0]-thisLayer.toWorld(thisLayer.anchorPoint,TIME-.01)[0])*100;
worldVelocityY = (thisLayer.toWorld(thisLayer.anchorPoint,TIME)[1]-thisLayer.toWorld(thisLayer.anchorPoint,TIME-.01)[1])*100;
return [worldVelocityX,worldVelocityY];
}
function worldSpeed(TIME) {
return length(worldVelocity(TIME));
}
timeStart = 0;
timeRestart = 0;
stop = false;
stopped = false;
for (i=timeToFrames(time);i>=0;i--) {
var t = framesToTime(i);
var tNext = t-frameDur;
if (worldSpeed(t) == 0 ) {
if (timeRestart == 0) timeRestart = t;
if (worldSpeed(tNext) !=0 ) {
timeStart = tNext;
break;
}
}
}
TIME = time-timeStart;
frameRestart = timeToFrames( time-timeRestart);
w = value
if ( frameRestart <= delay)
w = value - worldVelocity(time)*weight*(frameRestart/delay);
else
w = value - worldVelocity(time)*weight;
if (worldSpeed(time) == 0) {
spring = worldVelocity(timeStart) * ( .15/freq * Math.sin(freq * TIME * 2 * Math.PI) / Math.exp( TIME * decay ) );
w + spring;
}else{ w; } -
Remi Monedi
February 28, 2020 at 4:43 pmOk, I find a way to adapt this expression to my need. I wasn’t familiar with the .toWorld function.
I replaced the 2 lines I was talking earlier by these 2 and it works fine.worldVelocityX = (thisComp.layer(“A”).toWorld(thisComp.layer(“A”).position,TIME)[0]-thisComp.layer(“A”).toWorld(thisComp.layer(“A”).position,TIME-.01)[0])*100;
worldVelocityY = (thisComp.layer(“A”).toWorld(thisComp.layer(“A”).position,TIME)[1]-thisComp.layer(“A”).toWorld(thisComp.layer(“A”).position,TIME-.01)[1])*100;Now, no matter how the position of a layer “A” is animated, the position of the layer “B” will follow it with an “overshoot” trajectory. Yay!
But clearly, I don’t understand how it works. And I think this expression is only for 2D position parameter. I don’t think it could apply for Rotation or scale (there is length function on line 14). Where the previous expression from Dan was simpler and shorter even if I missed some elements to make it perfectly automated.
SO, I’m still wondering if I can “automate” the overshoot expression from Dan, OR if this long expression from Duik is better for what I want.
Any thoughts?decay = 5;
freq = 3;
if (decay == 0) decay = 0.1;
if (freq == 0) freq = 0.1;
delay = freq/decay;
weight = 1/decay/10;
frameDur = thisComp.frameDuration;
function worldVelocity(TIME) {
worldVelocityX = (thisComp.layer("A").toWorld(thisComp.layer("A").position,TIME)[0]-thisComp.layer("A").toWorld(thisComp.layer("A").position,TIME-.01)[0])*100;
worldVelocityY = (thisComp.layer("A").toWorld(thisComp.layer("A").position,TIME)[1]-thisComp.layer("A").toWorld(thisComp.layer("A").position,TIME-.01)[1])*100;
return [worldVelocityX,worldVelocityY];
}
function worldSpeed(TIME) {
return length(worldVelocity(TIME));
}
timeStart = 0;
timeRestart = 0;
stop = false;
stopped = false;
for (i=timeToFrames(time);i>=0;i--) {
var t = framesToTime(i);
var tNext = t-frameDur;
if (worldSpeed(t) == 0 ) {
if (timeRestart == 0) timeRestart = t;
if (worldSpeed(tNext) !=0 ) {
timeStart = tNext;
break;
}
}
}
TIME = time-timeStart;
frameRestart = timeToFrames( time-timeRestart);
w = value
if ( frameRestart <= delay)
w = value - worldVelocity(time)*weight*(frameRestart/delay);
else
w = value - worldVelocity(time)*weight;
if (worldSpeed(time) == 0) {
spring = worldVelocity(timeStart) * ( .15/freq * Math.sin(freq * TIME * 2 * Math.PI) / Math.exp( TIME * decay ) );
w + spring + thisComp.layer("A").transform.position;
}else{ w + thisComp.layer("A").transform.position; }
-
Jeff Gill
August 21, 2024 at 6:12 pmI’ve been looking for a solution to a similar problem and am so glad I discovered this post! I’m curious if you might be able to upload a working AE file that uses your approach as an example? I’ve tried copy and pasting your posted expressions into both position and anchor points, but I get met with some errors am finding difficult to debug. An expression that provides lag/overshoot without relying on keyframes is exactly what I’m after, and if you’ve found something that works, I’d be extremely grateful for any help you can give!
-
Dan Ebberts
August 21, 2024 at 10:04 pmIt would help if you could describe what’s generating the animation and what you want to happen.
-
Jeff Gill
August 22, 2024 at 2:40 amI figured out the issue! The line of code that reads “if ( frameRestart <= delay)” had somehow replaced “<” with “<” so replacing the character ended up doing the trick 🙂
What I’m aiming to accomplish is automated rotation overshoot for a 3D layer when it is parented to something that rotates and changes position, so this code has given me something to start with. The examples that you’ve posted on CC over the years and detailed descriptions on motionscript.com have been SUPER helpful as always, but if there are any classic Dan solutions to this problem that I haven’t been able to unearth, I’d love to know!
-
Dan Ebberts
August 22, 2024 at 6:49 amYou could play around with this position expression. It’s not perfect, but it might work for you:
f = 3;
d = 4;
att = 1.0;
nst = 38;
acc = [0,0];
st = thisComp.frameDuration;
t = time;
w = f*Math.PI*2;
p1 = toWorld(anchorPoint,t+st/2);
for (i = nst; i > 0; i--){
p0 = toWorld(anchorPoint,t);
v2 = (p1-p0);
pm1 = toWorld(anchorPoint,t-st/2);
v1 = (p0-pm1);
a = -(v2 - v1);
del = time - t;
if (length(a) > .01){
acc += a*Math.sin(del*w)/Math.exp(del*d);
}
t -= st;
p1 = pm1;
}
acc /= att;
value + fromWorldVec(acc);
Reply to this Discussion! Login or Sign Up