Skip to content

Instantly share code, notes, and snippets.

@grishka
Created February 18, 2023 03:06
Show Gist options
  • Save grishka/83755b852a1968b8a98e2153eb5c060f to your computer and use it in GitHub Desktop.
Save grishka/83755b852a1968b8a98e2153eb5c060f to your computer and use it in GitHub Desktop.
After Effects cubic-bezier exporter
// Source: https://community.adobe.com/t5/after-effects-discussions/convert-ae-keyframes-info-to-cubicbezier-points/m-p/6139286#M135604
// Usage: select a layer, then File -> Scripts -> Run Script File... -> select this script
var curItem = app.project.activeItem;
var selectedLayers = curItem.selectedLayers;
var selectedProperties = curItem.selectedProperties;
var output="";
if (selectedLayers == 0) {
output="Please Select at least one Layer";
}
else if (selectedLayers != 0) {
for (var i = 0; i < selectedLayers.length; i++) {
for (var f in selectedProperties) {
var currentProperty = selectedProperties[f];
if (currentProperty.numKeys > 1) {
for (var i = 1; i < currentProperty.numKeys; i++) {
var t1 = currentProperty.keyTime(i);
var t2 = currentProperty.keyTime(i + 1);
var val1 = currentProperty.keyValue(i);
var val2 = currentProperty.keyValue(i + 1);
//converting float to array
if (val1.constructor !== Array) {
var temp = new Array();
temp[0] = val1;
val1 = temp;
}
if (val2.constructor !== Array) {
var temp = new Array();
temp[0] = val2;
val2 = temp;
}
//multi dimension values
for (var y = 0; y < val1.length; y++) {
var delta_t = t2 - t1;
var c_val1 = parseFloat(val1[y]);
var c_val2 = parseFloat(val2[y]);
var delta = c_val2 - c_val1;
avSpeed = Math.abs(delta) / (delta_t);
/* $.write("val1 " + c_val1 + "--");
$.write("val2 " + c_val2 + "\n");
$.write("avSpeed " + avSpeed + "\n");*/
output+=('dimension ' + y + ':\n');
//values dispatch
if (c_val1 != c_val2) {
getBezier(currentProperty, i, c_val1, c_val2, avSpeed);
}
else { //if (c_val1 == c_val2)
var minmax = getMinMaxPoints(t1, t2, currentProperty, curItem);
var maxp_val = minmax.maxPoint.value;
var hasmax = true;
var hasmin = true;
var minp_val = minmax.minPoint.value;
if (!(c_val1 < maxp_val) || !(c_val1 > minp_val)) {
if (c_val1 < maxp_val) {
//create temp keyframe to "cut" the curve
var tempkey_i = currentProperty.addKey(minmax.maxPoint.time);
var tempavSpeed = Math.abs(maxp_val - c_val1) / (minmax.maxPoint.time - t1);
//get the 2 bezier-curves
getBezier(currentProperty, i, c_val1, maxp_val, tempavSpeed);//part going up
getBezier(currentProperty, tempkey_i, maxp_val, c_val1, tempavSpeed);//part going down
currentProperty.removeKey(tempkey_i);//remove temp key
}else {
hasmax = false;
}
if (c_val1 > minp_val) {
var tempkey_i = currentProperty.addKey(minmax.minPoint.time);
var tempavSpeed = Math.abs(maxp_val - c_val1) / (minmax.minPoint.time - t1);
getBezier(currentProperty, i, c_val1, minp_val, tempavSpeed);//part going down
getBezier(currentProperty, tempkey_i, minp_val, c_val1, tempavSpeed);//part going up
currentProperty.removeKey(tempkey_i);//remove temp key
}else{
hasmin = false;
}
if(!hasmax || !hasmin)//if flat curve
output+=("keyframe " + i + " has no animation\n\n");
} else { //if (c_val1 < maxp_val && c_val1 > minp_val)
//sort values
output+=('equal minmax ');
var first_time, sec_time, first_val, sec_val;
if (minmax.maxPoint.time < minmax.minPoint.time) {
//max value if first:
first_time = minmax.maxPoint.time;
first_val = maxp_val;
sec_time = minmax.minPoint.time;
sec_val = minp_val;
} else {
//min value if first:
first_time = minmax.minPoint.time;
first_val = minp_val;
sec_time = minmax.maxPoint.time;
sec_val = maxp_val;
}
// we have 2 extreme points, we need to "cut" twince (it gives 3 curves)
var tempkey_i = currentProperty.addKey(first_time);
var tempkey2_i = currentProperty.addKey(sec_time);
var tempavSpeed_1 = Math.abs(first_val - c_val1) / (first_time - t1);//avspeed 1rst curve
var tempavSpeed_2 = Math.abs(sec_val - first_val) / (sec_time - first_time);//avspeed 1nd curve
var tempavSpeed_3 = Math.abs(c_val2 - sec_val) / (t2 - sec_time);//avspeed 3rd curve
//get the 3 bezier-curves
getBezier(currentProperty, i, c_val1, first_val, tempavSpeed_1);
getBezier(currentProperty, tempkey_i, first_val, sec_val, tempavSpeed_2);
getBezier(currentProperty, tempkey2_i, sec_val, c_val2, tempavSpeed_3);
currentProperty.removeKey(tempkey_i);//remove temp key
currentProperty.removeKey(tempkey2_i);//remove temp key
}
}//end value dispatch
}
}
}
}
}
}
alert(output);
function getMinMaxPoints(t1, t2, currentProperty, curItem) {
var P = currentProperty;
var t = t1;
var min = 999999;
var max = -999999;
var tmin = 0;
var tmax = 0;
while (t <= t2) {
var val = P.valueAtTime(t, true);
if (val > max) {
max = val;
tmax = t;
}
if (val < min) {
min = val;
tmin = t;
}
t += curItem.frameDuration;
}
if (max == -999999)
max = P.valueAtTime(t1, true);
if (min == 999999)
min = P.valueAtTime(t1, true);
var maxPoint = {
value: max,
time: tmax
}
var minPoint = {
value: min,
time: tmin
}
return { minPoint: minPoint, maxPoint: maxPoint }
}
function getBezier(currentProperty, i, c_val1, c_val2, avSpeed) {
if (c_val1 < c_val2) {
x1 = currentProperty.keyOutTemporalEase(i)[0].influence / 100;
y1 = x1 * currentProperty.keyOutTemporalEase(i)[0].speed / avSpeed;
x2 = 1 - currentProperty.keyInTemporalEase(i + 1)[0].influence / 100;
y2 = 1 - (1 - x2) * (currentProperty.keyInTemporalEase(i + 1)[0].speed / avSpeed);
}
if (c_val2 < c_val1) {
x1 = currentProperty.keyOutTemporalEase(i)[0].influence / 100;
y1 = (-x1) * currentProperty.keyOutTemporalEase(i)[0].speed / avSpeed;
x2 = currentProperty.keyInTemporalEase(i + 1)[0].influence / 100;
y2 = 1 + x2 * (currentProperty.keyInTemporalEase(i + 1)[0].speed / avSpeed);
x2 = 1 - x2;
}
output+=("keyframe: " + i + " Cubic-bezier[" + x1.toFixed(2) + ", " + y1.toFixed(2) + ", " + x2.toFixed(2) + ", " + y2.toFixed(2) + "]\n\n");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment