import defaultValue from "./defaultValue.js"; import defined from "./defined.js"; import DeveloperError from "./DeveloperError.js"; import CesiumMath from "./Math.js"; const factorial = CesiumMath.factorial; function calculateCoefficientTerm( x, zIndices, xTable, derivOrder, termOrder, reservedIndices ) { let result = 0; let reserved; let i; let j; if (derivOrder > 0) { for (i = 0; i < termOrder; i++) { reserved = false; for (j = 0; j < reservedIndices.length && !reserved; j++) { if (i === reservedIndices[j]) { reserved = true; } } if (!reserved) { reservedIndices.push(i); result += calculateCoefficientTerm( x, zIndices, xTable, derivOrder - 1, termOrder, reservedIndices ); reservedIndices.splice(reservedIndices.length - 1, 1); } } return result; } result = 1; for (i = 0; i < termOrder; i++) { reserved = false; for (j = 0; j < reservedIndices.length && !reserved; j++) { if (i === reservedIndices[j]) { reserved = true; } } if (!reserved) { result *= x - xTable[zIndices[i]]; } } return result; } /** * An {@link InterpolationAlgorithm} for performing Hermite interpolation. * * @namespace HermitePolynomialApproximation */ const HermitePolynomialApproximation = { type: "Hermite", }; /** * Given the desired degree, returns the number of data points required for interpolation. * * @param {Number} degree The desired degree of interpolation. * @param {Number} [inputOrder=0] The order of the inputs (0 means just the data, 1 means the data and its derivative, etc). * @returns {Number} The number of required data points needed for the desired degree of interpolation. * @exception {DeveloperError} degree must be 0 or greater. * @exception {DeveloperError} inputOrder must be 0 or greater. */ HermitePolynomialApproximation.getRequiredDataPoints = function ( degree, inputOrder ) { inputOrder = defaultValue(inputOrder, 0); //>>includeStart('debug', pragmas.debug); if (!defined(degree)) { throw new DeveloperError("degree is required."); } if (degree < 0) { throw new DeveloperError("degree must be 0 or greater."); } if (inputOrder < 0) { throw new DeveloperError("inputOrder must be 0 or greater."); } //>>includeEnd('debug'); return Math.max(Math.floor((degree + 1) / (inputOrder + 1)), 2); }; /** * Interpolates values using Hermite Polynomial Approximation. * * @param {Number} x The independent variable for which the dependent variables will be interpolated. * @param {Number[]} xTable The array of independent variables to use to interpolate. The values * in this array must be in increasing order and the same value must not occur twice in the array. * @param {Number[]} yTable The array of dependent variables to use to interpolate. For a set of three * dependent values (p,q,w) at time 1 and time 2 this should be as follows: {p1, q1, w1, p2, q2, w2}. * @param {Number} yStride The number of dependent variable values in yTable corresponding to * each independent variable value in xTable. * @param {Number[]} [result] An existing array into which to store the result. * @returns {Number[]} The array of interpolated values, or the result parameter if one was provided. */ HermitePolynomialApproximation.interpolateOrderZero = function ( x, xTable, yTable, yStride, result ) { if (!defined(result)) { result = new Array(yStride); } let i; let j; let d; let s; let len; let index; const length = xTable.length; const coefficients = new Array(yStride); for (i = 0; i < yStride; i++) { result[i] = 0; const l = new Array(length); coefficients[i] = l; for (j = 0; j < length; j++) { l[j] = []; } } const zIndicesLength = length, zIndices = new Array(zIndicesLength); for (i = 0; i < zIndicesLength; i++) { zIndices[i] = i; } let highestNonZeroCoef = length - 1; for (s = 0; s < yStride; s++) { for (j = 0; j < zIndicesLength; j++) { index = zIndices[j] * yStride + s; coefficients[s][0].push(yTable[index]); } for (i = 1; i < zIndicesLength; i++) { let nonZeroCoefficients = false; for (j = 0; j < zIndicesLength - i; j++) { const zj = xTable[zIndices[j]]; const zn = xTable[zIndices[j + i]]; let numerator; if (zn - zj <= 0) { index = zIndices[j] * yStride + yStride * i + s; numerator = yTable[index]; coefficients[s][i].push(numerator / factorial(i)); } else { numerator = coefficients[s][i - 1][j + 1] - coefficients[s][i - 1][j]; coefficients[s][i].push(numerator / (zn - zj)); } nonZeroCoefficients = nonZeroCoefficients || numerator !== 0; } if (!nonZeroCoefficients) { highestNonZeroCoef = i - 1; } } } for (d = 0, len = 0; d <= len; d++) { for (i = d; i <= highestNonZeroCoef; i++) { const tempTerm = calculateCoefficientTerm(x, zIndices, xTable, d, i, []); for (s = 0; s < yStride; s++) { const coeff = coefficients[s][i][0]; result[s + d * yStride] += coeff * tempTerm; } } } return result; }; const arrayScratch = []; /** * Interpolates values using Hermite Polynomial Approximation. * * @param {Number} x The independent variable for which the dependent variables will be interpolated. * @param {Number[]} xTable The array of independent variables to use to interpolate. The values * in this array must be in increasing order and the same value must not occur twice in the array. * @param {Number[]} yTable The array of dependent variables to use to interpolate. For a set of three * dependent values (p,q,w) at time 1 and time 2 this should be as follows: {p1, q1, w1, p2, q2, w2}. * @param {Number} yStride The number of dependent variable values in yTable corresponding to * each independent variable value in xTable. * @param {Number} inputOrder The number of derivatives supplied for input. * @param {Number} outputOrder The number of derivatives desired for output. * @param {Number[]} [result] An existing array into which to store the result. * * @returns {Number[]} The array of interpolated values, or the result parameter if one was provided. */ HermitePolynomialApproximation.interpolate = function ( x, xTable, yTable, yStride, inputOrder, outputOrder, result ) { const resultLength = yStride * (outputOrder + 1); if (!defined(result)) { result = new Array(resultLength); } for (let r = 0; r < resultLength; r++) { result[r] = 0; } const length = xTable.length; // The zIndices array holds copies of the addresses of the xTable values // in the range we're looking at. Even though this just holds information already // available in xTable this is a much more convenient format. const zIndices = new Array(length * (inputOrder + 1)); let i; for (i = 0; i < length; i++) { for (let j = 0; j < inputOrder + 1; j++) { zIndices[i * (inputOrder + 1) + j] = i; } } const zIndiceslength = zIndices.length; const coefficients = arrayScratch; const highestNonZeroCoef = fillCoefficientList( coefficients, zIndices, xTable, yTable, yStride, inputOrder ); const reservedIndices = []; const tmp = (zIndiceslength * (zIndiceslength + 1)) / 2; const loopStop = Math.min(highestNonZeroCoef, outputOrder); for (let d = 0; d <= loopStop; d++) { for (i = d; i <= highestNonZeroCoef; i++) { reservedIndices.length = 0; const tempTerm = calculateCoefficientTerm( x, zIndices, xTable, d, i, reservedIndices ); const dimTwo = Math.floor((i * (1 - i)) / 2) + zIndiceslength * i; for (let s = 0; s < yStride; s++) { const dimOne = Math.floor(s * tmp); const coef = coefficients[dimOne + dimTwo]; result[s + d * yStride] += coef * tempTerm; } } } return result; }; function fillCoefficientList( coefficients, zIndices, xTable, yTable, yStride, inputOrder ) { let j; let index; let highestNonZero = -1; const zIndiceslength = zIndices.length; const tmp = (zIndiceslength * (zIndiceslength + 1)) / 2; for (let s = 0; s < yStride; s++) { const dimOne = Math.floor(s * tmp); for (j = 0; j < zIndiceslength; j++) { index = zIndices[j] * yStride * (inputOrder + 1) + s; coefficients[dimOne + j] = yTable[index]; } for (let i = 1; i < zIndiceslength; i++) { let coefIndex = 0; const dimTwo = Math.floor((i * (1 - i)) / 2) + zIndiceslength * i; let nonZeroCoefficients = false; for (j = 0; j < zIndiceslength - i; j++) { const zj = xTable[zIndices[j]]; const zn = xTable[zIndices[j + i]]; let numerator; let coefficient; if (zn - zj <= 0) { index = zIndices[j] * yStride * (inputOrder + 1) + yStride * i + s; numerator = yTable[index]; coefficient = numerator / CesiumMath.factorial(i); coefficients[dimOne + dimTwo + coefIndex] = coefficient; coefIndex++; } else { const dimTwoMinusOne = Math.floor(((i - 1) * (2 - i)) / 2) + zIndiceslength * (i - 1); numerator = coefficients[dimOne + dimTwoMinusOne + j + 1] - coefficients[dimOne + dimTwoMinusOne + j]; coefficient = numerator / (zn - zj); coefficients[dimOne + dimTwo + coefIndex] = coefficient; coefIndex++; } nonZeroCoefficients = nonZeroCoefficients || numerator !== 0.0; } if (nonZeroCoefficients) { highestNonZero = Math.max(highestNonZero, i); } } } return highestNonZero; } export default HermitePolynomialApproximation;