/* 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; var _lottieGlobal = {}; seedrandom(BMMath); function resetFrame() { _lottieGlobal = {}; } function $bm_isInstanceOfArray(arr) { return arr.constructor === Array || arr.constructor === Float32Array; } function isNumerable(tOfV, v) { return tOfV === 'number' || v instanceof Number || tOfV === 'boolean' || tOfV === 'string'; } function $bm_neg(a) { var tOfA = typeof a; if (tOfA === 'number' || a instanceof Number || tOfA === 'boolean') { 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 ((isNumerable(tOfA, a) && isNumerable(tOfB, b)) || tOfA === 'string' || tOfB === 'string') { 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) { // Bail out if we don't want expressions function noOp(_value) { return _value; } if (!elem.globalData.renderConfig.runExpressions) { return noOp; } 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 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, _lottieGlobal]; ob.resetFrame = resetFrame; return ob; }()); export default ExpressionManager;