blob: 5cab2442c3619a2b9aed7cf957b977832369b7d2 [file] [log] [blame]
/* eslint-disable camelcase */
import {
degToRads,
BMMath,
} from '../common';
import {
createTypedArray,
} from '../helpers/arrays';
import BezierFactory from '../../3rd_party/BezierEaser';
import shapePool from '../pooling/shape_pool';
import seedrandom from '../../3rd_party/seedrandom';
import propTypes from '../helpers/propTypes';
const ExpressionManager = (function () {
'use strict';
var ob = {};
var Math = BMMath;
var window = null;
var document = null;
var XMLHttpRequest = null;
var fetch = null;
var frames = null;
seedrandom(BMMath);
function $bm_isInstanceOfArray(arr) {
return arr.constructor === Array || arr.constructor === Float32Array;
}
function isNumerable(tOfV, v) {
return tOfV === 'number' || tOfV === 'boolean' || tOfV === 'string' || v instanceof Number;
}
function $bm_neg(a) {
var tOfA = typeof a;
if (tOfA === 'number' || tOfA === 'boolean' || a instanceof Number) {
return -a;
}
if ($bm_isInstanceOfArray(a)) {
var i;
var lenA = a.length;
var retArr = [];
for (i = 0; i < lenA; i += 1) {
retArr[i] = -a[i];
}
return retArr;
}
if (a.propType) {
return a.v;
}
return -a;
}
var easeInBez = BezierFactory.getBezierEasing(0.333, 0, 0.833, 0.833, 'easeIn').get;
var easeOutBez = BezierFactory.getBezierEasing(0.167, 0.167, 0.667, 1, 'easeOut').get;
var easeInOutBez = BezierFactory.getBezierEasing(0.33, 0, 0.667, 1, 'easeInOut').get;
function sum(a, b) {
var tOfA = typeof a;
var tOfB = typeof b;
if (tOfA === 'string' || tOfB === 'string') {
return a + b;
}
if (isNumerable(tOfA, a) && isNumerable(tOfB, b)) {
return a + b;
}
if ($bm_isInstanceOfArray(a) && isNumerable(tOfB, b)) {
a = a.slice(0);
a[0] += b;
return a;
}
if (isNumerable(tOfA, a) && $bm_isInstanceOfArray(b)) {
b = b.slice(0);
b[0] = a + b[0];
return b;
}
if ($bm_isInstanceOfArray(a) && $bm_isInstanceOfArray(b)) {
var i = 0;
var lenA = a.length;
var lenB = b.length;
var retArr = [];
while (i < lenA || i < lenB) {
if ((typeof a[i] === 'number' || a[i] instanceof Number) && (typeof b[i] === 'number' || b[i] instanceof Number)) {
retArr[i] = a[i] + b[i];
} else {
retArr[i] = b[i] === undefined ? a[i] : a[i] || b[i];
}
i += 1;
}
return retArr;
}
return 0;
}
var add = sum;
function sub(a, b) {
var tOfA = typeof a;
var tOfB = typeof b;
if (isNumerable(tOfA, a) && isNumerable(tOfB, b)) {
if (tOfA === 'string') {
a = parseInt(a, 10);
}
if (tOfB === 'string') {
b = parseInt(b, 10);
}
return a - b;
}
if ($bm_isInstanceOfArray(a) && isNumerable(tOfB, b)) {
a = a.slice(0);
a[0] -= b;
return a;
}
if (isNumerable(tOfA, a) && $bm_isInstanceOfArray(b)) {
b = b.slice(0);
b[0] = a - b[0];
return b;
}
if ($bm_isInstanceOfArray(a) && $bm_isInstanceOfArray(b)) {
var i = 0;
var lenA = a.length;
var lenB = b.length;
var retArr = [];
while (i < lenA || i < lenB) {
if ((typeof a[i] === 'number' || a[i] instanceof Number) && (typeof b[i] === 'number' || b[i] instanceof Number)) {
retArr[i] = a[i] - b[i];
} else {
retArr[i] = b[i] === undefined ? a[i] : a[i] || b[i];
}
i += 1;
}
return retArr;
}
return 0;
}
function mul(a, b) {
var tOfA = typeof a;
var tOfB = typeof b;
var arr;
if (isNumerable(tOfA, a) && isNumerable(tOfB, b)) {
return a * b;
}
var i;
var len;
if ($bm_isInstanceOfArray(a) && isNumerable(tOfB, b)) {
len = a.length;
arr = createTypedArray('float32', len);
for (i = 0; i < len; i += 1) {
arr[i] = a[i] * b;
}
return arr;
}
if (isNumerable(tOfA, a) && $bm_isInstanceOfArray(b)) {
len = b.length;
arr = createTypedArray('float32', len);
for (i = 0; i < len; i += 1) {
arr[i] = a * b[i];
}
return arr;
}
return 0;
}
function div(a, b) {
var tOfA = typeof a;
var tOfB = typeof b;
var arr;
if (isNumerable(tOfA, a) && isNumerable(tOfB, b)) {
return a / b;
}
var i;
var len;
if ($bm_isInstanceOfArray(a) && isNumerable(tOfB, b)) {
len = a.length;
arr = createTypedArray('float32', len);
for (i = 0; i < len; i += 1) {
arr[i] = a[i] / b;
}
return arr;
}
if (isNumerable(tOfA, a) && $bm_isInstanceOfArray(b)) {
len = b.length;
arr = createTypedArray('float32', len);
for (i = 0; i < len; i += 1) {
arr[i] = a / b[i];
}
return arr;
}
return 0;
}
function mod(a, b) {
if (typeof a === 'string') {
a = parseInt(a, 10);
}
if (typeof b === 'string') {
b = parseInt(b, 10);
}
return a % b;
}
var $bm_sum = sum;
var $bm_sub = sub;
var $bm_mul = mul;
var $bm_div = div;
var $bm_mod = mod;
function clamp(num, min, max) {
if (min > max) {
var mm = max;
max = min;
min = mm;
}
return Math.min(Math.max(num, min), max);
}
function radiansToDegrees(val) {
return val / degToRads;
}
var radians_to_degrees = radiansToDegrees;
function degreesToRadians(val) {
return val * degToRads;
}
var degrees_to_radians = radiansToDegrees;
var helperLengthArray = [0, 0, 0, 0, 0, 0];
function length(arr1, arr2) {
if (typeof arr1 === 'number' || arr1 instanceof Number) {
arr2 = arr2 || 0;
return Math.abs(arr1 - arr2);
}
if (!arr2) {
arr2 = helperLengthArray;
}
var i;
var len = Math.min(arr1.length, arr2.length);
var addedLength = 0;
for (i = 0; i < len; i += 1) {
addedLength += Math.pow(arr2[i] - arr1[i], 2);
}
return Math.sqrt(addedLength);
}
function normalize(vec) {
return div(vec, length(vec));
}
function rgbToHsl(val) {
var r = val[0]; var g = val[1]; var b = val[2];
var max = Math.max(r, g, b);
var min = Math.min(r, g, b);
var h;
var s;
var l = (max + min) / 2;
if (max === min) {
h = 0; // achromatic
s = 0; // achromatic
} else {
var d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
default: break;
}
h /= 6;
}
return [h, s, l, val[3]];
}
function hue2rgb(p, q, t) {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
}
function hslToRgb(val) {
var h = val[0];
var s = val[1];
var l = val[2];
var r;
var g;
var b;
if (s === 0) {
r = l; // achromatic
b = l; // achromatic
g = l; // achromatic
} else {
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3);
}
return [r, g, b, val[3]];
}
function linear(t, tMin, tMax, value1, value2) {
if (value1 === undefined || value2 === undefined) {
value1 = tMin;
value2 = tMax;
tMin = 0;
tMax = 1;
}
if (tMax < tMin) {
var _tMin = tMax;
tMax = tMin;
tMin = _tMin;
}
if (t <= tMin) {
return value1;
} if (t >= tMax) {
return value2;
}
var perc = tMax === tMin ? 0 : (t - tMin) / (tMax - tMin);
if (!value1.length) {
return value1 + (value2 - value1) * perc;
}
var i;
var len = value1.length;
var arr = createTypedArray('float32', len);
for (i = 0; i < len; i += 1) {
arr[i] = value1[i] + (value2[i] - value1[i]) * perc;
}
return arr;
}
function random(min, max) {
if (max === undefined) {
if (min === undefined) {
min = 0;
max = 1;
} else {
max = min;
min = undefined;
}
}
if (max.length) {
var i;
var len = max.length;
if (!min) {
min = createTypedArray('float32', len);
}
var arr = createTypedArray('float32', len);
var rnd = BMMath.random();
for (i = 0; i < len; i += 1) {
arr[i] = min[i] + rnd * (max[i] - min[i]);
}
return arr;
}
if (min === undefined) {
min = 0;
}
var rndm = BMMath.random();
return min + rndm * (max - min);
}
function createPath(points, inTangents, outTangents, closed) {
var i;
var len = points.length;
var path = shapePool.newElement();
path.setPathData(!!closed, len);
var arrPlaceholder = [0, 0];
var inVertexPoint;
var outVertexPoint;
for (i = 0; i < len; i += 1) {
inVertexPoint = (inTangents && inTangents[i]) ? inTangents[i] : arrPlaceholder;
outVertexPoint = (outTangents && outTangents[i]) ? outTangents[i] : arrPlaceholder;
path.setTripleAt(points[i][0], points[i][1], outVertexPoint[0] + points[i][0], outVertexPoint[1] + points[i][1], inVertexPoint[0] + points[i][0], inVertexPoint[1] + points[i][1], i, true);
}
return path;
}
function initiateExpression(elem, data, property) {
var val = data.x;
var needsVelocity = /velocity(?![\w\d])/.test(val);
var _needsRandom = val.indexOf('random') !== -1;
var elemType = elem.data.ty;
var transform;
var $bm_transform;
var content;
var effect;
var thisProperty = property;
thisProperty.valueAtTime = thisProperty.getValueAtTime;
Object.defineProperty(thisProperty, 'value', {
get: function () {
return thisProperty.v;
},
});
elem.comp.frameDuration = 1 / elem.comp.globalData.frameRate;
elem.comp.displayStartTime = 0;
var inPoint = elem.data.ip / elem.comp.globalData.frameRate;
var outPoint = elem.data.op / elem.comp.globalData.frameRate;
var width = elem.data.sw ? elem.data.sw : 0;
var height = elem.data.sh ? elem.data.sh : 0;
var name = elem.data.nm;
var loopIn;
var loop_in;
var loopOut;
var loop_out;
var smooth;
var toWorld;
var fromWorld;
var fromComp;
var toComp;
var fromCompToSurface;
var position;
var rotation;
var anchorPoint;
var scale;
var thisLayer;
var thisComp;
var mask;
var valueAtTime;
var velocityAtTime;
var scoped_bm_rt;
// val = val.replace(/(\\?"|')((http)(s)?(:\/))?\/.*?(\\?"|')/g, "\"\""); // deter potential network calls
var expression_function = eval('[function _expression_function(){' + val + ';scoped_bm_rt=$bm_rt}]')[0]; // eslint-disable-line no-eval
var numKeys = property.kf ? data.k.length : 0;
var active = !this.data || this.data.hd !== true;
var wiggle = function wiggle(freq, amp) {
var iWiggle;
var j;
var lenWiggle = this.pv.length ? this.pv.length : 1;
var addedAmps = createTypedArray('float32', lenWiggle);
freq = 5;
var iterations = Math.floor(time * freq);
iWiggle = 0;
j = 0;
while (iWiggle < iterations) {
// var rnd = BMMath.random();
for (j = 0; j < lenWiggle; j += 1) {
addedAmps[j] += -amp + amp * 2 * BMMath.random();
// addedAmps[j] += -amp + amp*2*rnd;
}
iWiggle += 1;
}
// var rnd2 = BMMath.random();
var periods = time * freq;
var perc = periods - Math.floor(periods);
var arr = createTypedArray('float32', lenWiggle);
if (lenWiggle > 1) {
for (j = 0; j < lenWiggle; j += 1) {
arr[j] = this.pv[j] + addedAmps[j] + (-amp + amp * 2 * BMMath.random()) * perc;
// arr[j] = this.pv[j] + addedAmps[j] + (-amp + amp*2*rnd)*perc;
// arr[i] = this.pv[i] + addedAmp + amp1*perc + amp2*(1-perc);
}
return arr;
}
return this.pv + addedAmps[0] + (-amp + amp * 2 * BMMath.random()) * perc;
}.bind(this);
if (thisProperty.loopIn) {
loopIn = thisProperty.loopIn.bind(thisProperty);
loop_in = loopIn;
}
if (thisProperty.loopOut) {
loopOut = thisProperty.loopOut.bind(thisProperty);
loop_out = loopOut;
}
if (thisProperty.smooth) {
smooth = thisProperty.smooth.bind(thisProperty);
}
function loopInDuration(type, duration) {
return loopIn(type, duration, true);
}
function loopOutDuration(type, duration) {
return loopOut(type, duration, true);
}
if (this.getValueAtTime) {
valueAtTime = this.getValueAtTime.bind(this);
}
if (this.getVelocityAtTime) {
velocityAtTime = this.getVelocityAtTime.bind(this);
}
var comp = elem.comp.globalData.projectInterface.bind(elem.comp.globalData.projectInterface);
function lookAt(elem1, elem2) {
var fVec = [elem2[0] - elem1[0], elem2[1] - elem1[1], elem2[2] - elem1[2]];
var pitch = Math.atan2(fVec[0], Math.sqrt(fVec[1] * fVec[1] + fVec[2] * fVec[2])) / degToRads;
var yaw = -Math.atan2(fVec[1], fVec[2]) / degToRads;
return [yaw, pitch, 0];
}
function easeOut(t, tMin, tMax, val1, val2) {
return applyEase(easeOutBez, t, tMin, tMax, val1, val2);
}
function easeIn(t, tMin, tMax, val1, val2) {
return applyEase(easeInBez, t, tMin, tMax, val1, val2);
}
function ease(t, tMin, tMax, val1, val2) {
return applyEase(easeInOutBez, t, tMin, tMax, val1, val2);
}
function applyEase(fn, t, tMin, tMax, val1, val2) {
if (val1 === undefined) {
val1 = tMin;
val2 = tMax;
} else {
t = (t - tMin) / (tMax - tMin);
}
if (t > 1) {
t = 1;
} else if (t < 0) {
t = 0;
}
var mult = fn(t);
if ($bm_isInstanceOfArray(val1)) {
var iKey;
var lenKey = val1.length;
var arr = createTypedArray('float32', lenKey);
for (iKey = 0; iKey < lenKey; iKey += 1) {
arr[iKey] = (val2[iKey] - val1[iKey]) * mult + val1[iKey];
}
return arr;
}
return (val2 - val1) * mult + val1;
}
function nearestKey(time) {
var iKey;
var lenKey = data.k.length;
var index;
var keyTime;
if (!data.k.length || typeof (data.k[0]) === 'number') {
index = 0;
keyTime = 0;
} else {
index = -1;
time *= elem.comp.globalData.frameRate;
if (time < data.k[0].t) {
index = 1;
keyTime = data.k[0].t;
} else {
for (iKey = 0; iKey < lenKey - 1; iKey += 1) {
if (time === data.k[iKey].t) {
index = iKey + 1;
keyTime = data.k[iKey].t;
break;
} else if (time > data.k[iKey].t && time < data.k[iKey + 1].t) {
if (time - data.k[iKey].t > data.k[iKey + 1].t - time) {
index = iKey + 2;
keyTime = data.k[iKey + 1].t;
} else {
index = iKey + 1;
keyTime = data.k[iKey].t;
}
break;
}
}
if (index === -1) {
index = iKey + 1;
keyTime = data.k[iKey].t;
}
}
}
var obKey = {};
obKey.index = index;
obKey.time = keyTime / elem.comp.globalData.frameRate;
return obKey;
}
function key(ind) {
var obKey;
var iKey;
var lenKey;
if (!data.k.length || typeof (data.k[0]) === 'number') {
throw new Error('The property has no keyframe at index ' + ind);
}
ind -= 1;
obKey = {
time: data.k[ind].t / elem.comp.globalData.frameRate,
value: [],
};
var arr = Object.prototype.hasOwnProperty.call(data.k[ind], 's') ? data.k[ind].s : data.k[ind - 1].e;
lenKey = arr.length;
for (iKey = 0; iKey < lenKey; iKey += 1) {
obKey[iKey] = arr[iKey];
obKey.value[iKey] = arr[iKey];
}
return obKey;
}
function framesToTime(fr, fps) {
if (!fps) {
fps = elem.comp.globalData.frameRate;
}
return fr / fps;
}
function timeToFrames(t, fps) {
if (!t && t !== 0) {
t = time;
}
if (!fps) {
fps = elem.comp.globalData.frameRate;
}
return t * fps;
}
function seedRandom(seed) {
BMMath.seedrandom(randSeed + seed);
}
function sourceRectAtTime() {
return elem.sourceRectAtTime();
}
function substring(init, end) {
if (typeof value === 'string') {
if (end === undefined) {
return value.substring(init);
}
return value.substring(init, end);
}
return '';
}
function substr(init, end) {
if (typeof value === 'string') {
if (end === undefined) {
return value.substr(init);
}
return value.substr(init, end);
}
return '';
}
function posterizeTime(framesPerSecond) {
time = framesPerSecond === 0 ? 0 : Math.floor(time * framesPerSecond) / framesPerSecond;
value = valueAtTime(time);
}
var time;
var velocity;
var value;
var text;
var textIndex;
var textTotal;
var selectorValue;
var index = elem.data.ind;
var hasParent = !!(elem.hierarchy && elem.hierarchy.length);
var parent;
var randSeed = Math.floor(Math.random() * 1000000);
var globalData = elem.globalData;
function noOp(_value) {
return _value;
}
if (!globalData.renderConfig.runExpressions) {
return noOp;
}
function executeExpression(_value) {
// globalData.pushExpression();
value = _value;
if (this.frameExpressionId === elem.globalData.frameId && this.propType !== 'textSelector') {
return value;
}
if (this.propType === 'textSelector') {
textIndex = this.textIndex;
textTotal = this.textTotal;
selectorValue = this.selectorValue;
}
if (!thisLayer) {
text = elem.layerInterface.text;
thisLayer = elem.layerInterface;
thisComp = elem.comp.compInterface;
toWorld = thisLayer.toWorld.bind(thisLayer);
fromWorld = thisLayer.fromWorld.bind(thisLayer);
fromComp = thisLayer.fromComp.bind(thisLayer);
toComp = thisLayer.toComp.bind(thisLayer);
mask = thisLayer.mask ? thisLayer.mask.bind(thisLayer) : null;
fromCompToSurface = fromComp;
}
if (!transform) {
transform = elem.layerInterface('ADBE Transform Group');
$bm_transform = transform;
if (transform) {
anchorPoint = transform.anchorPoint;
/* position = transform.position;
rotation = transform.rotation;
scale = transform.scale; */
}
}
if (elemType === 4 && !content) {
content = thisLayer('ADBE Root Vectors Group');
}
if (!effect) {
effect = thisLayer(4);
}
hasParent = !!(elem.hierarchy && elem.hierarchy.length);
if (hasParent && !parent) {
parent = elem.hierarchy[0].layerInterface;
}
time = this.comp.renderedFrame / this.comp.globalData.frameRate;
if (_needsRandom) {
seedRandom(randSeed + time);
}
if (needsVelocity) {
velocity = velocityAtTime(time);
}
expression_function();
this.frameExpressionId = elem.globalData.frameId;
// TODO: Check if it's possible to return on ShapeInterface the .v value
// Changed this to a ternary operation because Rollup failed compiling it correctly
scoped_bm_rt = scoped_bm_rt.propType === propTypes.SHAPE
? scoped_bm_rt.v
: scoped_bm_rt;
return scoped_bm_rt;
}
// Bundlers will see these as dead code and unless we reference them
executeExpression.__preventDeadCodeRemoval = [$bm_transform, anchorPoint, time, velocity, inPoint, outPoint, width, height, name, loop_in, loop_out, smooth, toComp, fromCompToSurface, toWorld, fromWorld, mask, position, rotation, scale, thisComp, numKeys, active, wiggle, loopInDuration, loopOutDuration, comp, lookAt, easeOut, easeIn, ease, nearestKey, key, text, textIndex, textTotal, selectorValue, framesToTime, timeToFrames, sourceRectAtTime, substring, substr, posterizeTime, index, globalData];
return executeExpression;
}
ob.initiateExpression = initiateExpression;
ob.__preventDeadCodeRemoval = [window, document, XMLHttpRequest, fetch, frames, $bm_neg, add, $bm_sum, $bm_sub, $bm_mul, $bm_div, $bm_mod, clamp, radians_to_degrees, degreesToRadians, degrees_to_radians, normalize, rgbToHsl, hslToRgb, linear, random, createPath];
return ob;
}());
export default ExpressionManager;