@remotion/paths
Version:
Utilities for working with SVG paths
92 lines (91 loc) • 4.31 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.interpolateInstructions = interpolateInstructions;
const get_end_position_1 = require("../get-end-position");
const convert_to_same_instruction_type_1 = require("./convert-to-same-instruction-type");
const extend_command_1 = require("./extend-command");
const interpolate_instruction_of_same_kind_1 = require("./interpolate-instruction-of-same-kind");
/**
* Interpolate from A to B by extending A and B during interpolation to have
* the same number of points. This allows for a smooth transition when they
* have a different number of points.
*
* Ignores the `Z` command in paths unless both A and B end with it.
*
* This function works directly with arrays of command objects instead of with
* path `d` strings (see interpolatePath for working with `d` strings).
*
* @param {Object[]} aCommandsInput Array of path commands
* @param {Object[]} bCommandsInput Array of path commands
* @returns {Function} Interpolation function that maps t ([0, 1]) to an array of path commands.
*/
function interpolateInstructions(aCommandsInput, bCommandsInput) {
// make a copy so we don't mess with the input arrays
let aCommands = aCommandsInput.slice();
let bCommands = bCommandsInput.slice();
// both input sets are empty, so we don't interpolate
if (!aCommands.length && !bCommands.length) {
return function () {
return [];
};
}
// do we add Z during interpolation? yes if both have it. (we'd expect both to have it or not)
const addZ = (aCommands.length === 0 || aCommands[aCommands.length - 1].type === 'Z') &&
(bCommands.length === 0 || bCommands[bCommands.length - 1].type === 'Z');
// we temporarily remove Z
if (aCommands.length > 0 && aCommands[aCommands.length - 1].type === 'Z') {
aCommands.pop();
}
if (bCommands.length > 0 && bCommands[bCommands.length - 1].type === 'Z') {
bCommands.pop();
}
// if A is empty, treat it as if it used to contain just the first point
// of B. This makes it so the line extends out of from that first point.
if (!aCommands.length) {
aCommands.push(bCommands[0]);
// otherwise if B is empty, treat it as if it contains the first point
// of A. This makes it so the line retracts into the first point.
}
else if (!bCommands.length) {
bCommands.push(aCommands[0]);
}
// extend to match equal size
const numPointsToExtend = Math.abs(bCommands.length - aCommands.length);
if (numPointsToExtend !== 0) {
// B has more points than A, so add points to A before interpolating
if (bCommands.length > aCommands.length) {
aCommands = (0, extend_command_1.extendInstruction)(aCommands, bCommands);
// else if A has more points than B, add more points to B
}
else if (bCommands.length < aCommands.length) {
bCommands = (0, extend_command_1.extendInstruction)(bCommands, aCommands);
}
}
// commands have same length now.
// convert commands in A to the same type as those in B
const aSameType = aCommands.map((aCommand, i) => {
const commandsUntilNow = aCommands.slice(0, i);
const point = (0, get_end_position_1.getEndPosition)(commandsUntilNow);
return (0, convert_to_same_instruction_type_1.convertToSameInstructionType)(aCommand, bCommands[i], point);
});
const interpolatedCommands = [];
if (addZ) {
aSameType.push({ type: 'Z' }); // required for when returning at t == 0
bCommands.push({ type: 'Z' });
}
return function (t) {
// at 1 return the final value without the extensions used during interpolation
if (t === 1) {
return bCommandsInput;
}
// work with aCommands directly since interpolatedCommands are mutated
if (t === 0) {
return aSameType;
}
// interpolate the commands using the mutable interpolated command objs
for (let i = 0; i < aSameType.length; ++i) {
interpolatedCommands.push((0, interpolate_instruction_of_same_kind_1.interpolateInstructionOfSameKind)(t, aSameType[i], bCommands[i]));
}
return interpolatedCommands;
};
}