Source: utils/Easeing.js

import {Utils} from './Utils';
import {findStep, inRange} from '../aftereffect/common/common';
import {BezierEasing} from '../math/BezierEasing';
const bezierPool = {};

/**
 * 准备好贝塞尔曲线
 * @param {number} mX1 控制点1的x分量
 * @param {number} mY1 控制点1的y分量
 * @param {number} mX2 控制点2的x分量
 * @param {number} mY2 控制点2的y分量
 * @param {string} nm 控制点命名
 * @return {BezierEasing}
 */
function prepareEaseing(mX1, mY1, mX2, mY2, nm) {
  const str = nm || [mX2, mY2, mX1, mY1].join('_').replace(/\./g, 'p');
  if (bezierPool[str]) {
    return bezierPool[str];
  }
  const bezEasing = new BezierEasing(mX1, mY1, mX2, mY2);
  bezierPool[str] = bezEasing;
}

/**
 * 根据进度获取到普通插值
 * @param {number} s  插值起始端点
 * @param {number} e  插值结束端点
 * @param {array}  nm 贝塞尔曲线的名字
 * @param {number} p  插值进度
 * @return {array}
 */
function getEaseing(s, e, nm, p) {
  const value = [];
  for (let i = 0; i < s.length; i++) {
    const bezier = bezierPool[nm[i]] || bezierPool[nm[0]];
    const rate = bezier.get(p);
    const v = e[i] - s[i];
    value[i] = s[i] + v * rate;
  }
  return value;
}

/**
 *
 * @param {BezierCurve} curve
 * @param {string} nm
 * @param {number} p
 * @return {Point}
 */
function getEaseingPath(curve, nm, p) {
  const rate = bezierPool[nm].get(p);
  const point = curve.getPointAt(rate);
  return [point.x, point.y, point.z];
}

/**
 * interpolation keyframes and return value
 * @ignore
 * @param {object} keyframes keyframes with special prop
 * @param {number} progress progress
 * @param {object} indexCache index cache
 * @param {string} key prop name
 * @return {array}
 */
function interpolation(keyframes, progress, indexCache, key) {
  const aksk = keyframes.k;
  if (keyframes.expression) {
    progress = keyframes.expression.update(progress);
  }
  if (progress <= keyframes.ost) {
    return aksk[0].s;
  } else if (progress >= keyframes.oet) {
    const last = aksk.length - 2;
    return aksk[last].e;
  } else {
    let ick = indexCache[key];
    let frame = aksk[ick];
    if (
      !Utils.isNumber(ick) ||
      !inRange(progress, frame.ost, frame.oet)
    ) {
      ick = indexCache[key] = findStep(aksk, progress);
      frame = aksk[ick];
    }
    const rate = (progress - frame.ost) / (frame.oet - frame.ost);
    if (frame.curve) {
      return getEaseingPath(frame.curve, frame.n, rate);
    } else {
      return getEaseing(frame.s, frame.e, frame.n, rate);
    }
  }
}

export {prepareEaseing, getEaseing, getEaseingPath, interpolation};