| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620 | import Cartesian3 from "./Cartesian3.js";import Cartesian4 from "./Cartesian4.js";import defaultValue from "./defaultValue.js";import defined from "./defined.js";import DeveloperError from "./DeveloperError.js";import LinearSpline from "./LinearSpline.js";import Matrix4 from "./Matrix4.js";import Spline from "./Spline.js";import TridiagonalSystemSolver from "./TridiagonalSystemSolver.js";const scratchLower = [];const scratchDiagonal = [];const scratchUpper = [];const scratchRight = [];function generateClamped(points, firstTangent, lastTangent) {  const l = scratchLower;  const u = scratchUpper;  const d = scratchDiagonal;  const r = scratchRight;  l.length = u.length = points.length - 1;  d.length = r.length = points.length;  let i;  l[0] = d[0] = 1.0;  u[0] = 0.0;  let right = r[0];  if (!defined(right)) {    right = r[0] = new Cartesian3();  }  Cartesian3.clone(firstTangent, right);  for (i = 1; i < l.length - 1; ++i) {    l[i] = u[i] = 1.0;    d[i] = 4.0;    right = r[i];    if (!defined(right)) {      right = r[i] = new Cartesian3();    }    Cartesian3.subtract(points[i + 1], points[i - 1], right);    Cartesian3.multiplyByScalar(right, 3.0, right);  }  l[i] = 0.0;  u[i] = 1.0;  d[i] = 4.0;  right = r[i];  if (!defined(right)) {    right = r[i] = new Cartesian3();  }  Cartesian3.subtract(points[i + 1], points[i - 1], right);  Cartesian3.multiplyByScalar(right, 3.0, right);  d[i + 1] = 1.0;  right = r[i + 1];  if (!defined(right)) {    right = r[i + 1] = new Cartesian3();  }  Cartesian3.clone(lastTangent, right);  return TridiagonalSystemSolver.solve(l, d, u, r);}function generateNatural(points) {  const l = scratchLower;  const u = scratchUpper;  const d = scratchDiagonal;  const r = scratchRight;  l.length = u.length = points.length - 1;  d.length = r.length = points.length;  let i;  l[0] = u[0] = 1.0;  d[0] = 2.0;  let right = r[0];  if (!defined(right)) {    right = r[0] = new Cartesian3();  }  Cartesian3.subtract(points[1], points[0], right);  Cartesian3.multiplyByScalar(right, 3.0, right);  for (i = 1; i < l.length; ++i) {    l[i] = u[i] = 1.0;    d[i] = 4.0;    right = r[i];    if (!defined(right)) {      right = r[i] = new Cartesian3();    }    Cartesian3.subtract(points[i + 1], points[i - 1], right);    Cartesian3.multiplyByScalar(right, 3.0, right);  }  d[i] = 2.0;  right = r[i];  if (!defined(right)) {    right = r[i] = new Cartesian3();  }  Cartesian3.subtract(points[i], points[i - 1], right);  Cartesian3.multiplyByScalar(right, 3.0, right);  return TridiagonalSystemSolver.solve(l, d, u, r);}/** * A Hermite spline is a cubic interpolating spline. Points, incoming tangents, outgoing tangents, and times * must be defined for each control point. The outgoing tangents are defined for points [0, n - 2] and the incoming * tangents are defined for points [1, n - 1]. For example, when interpolating a segment of the curve between <code>points[i]</code> and * <code>points[i + 1]</code>, the tangents at the points will be <code>outTangents[i]</code> and <code>inTangents[i]</code>, * respectively. * * @alias HermiteSpline * @constructor * * @param {Object} options Object with the following properties: * @param {Number[]} options.times An array of strictly increasing, unit-less, floating-point times at each point. *                The values are in no way connected to the clock time. They are the parameterization for the curve. * @param {Cartesian3[]} options.points The array of control points. * @param {Cartesian3[]} options.inTangents The array of incoming tangents at each control point. * @param {Cartesian3[]} options.outTangents The array of outgoing tangents at each control point. * * @exception {DeveloperError} points.length must be greater than or equal to 2. * @exception {DeveloperError} times.length must be equal to points.length. * @exception {DeveloperError} inTangents and outTangents must have a length equal to points.length - 1. * @exception {DeveloperError} inTangents and outTangents must be of the same type as points. * * @example * // Create a G<sup>1</sup> continuous Hermite spline * const times = [ 0.0, 1.5, 3.0, 4.5, 6.0 ]; * const spline = new Cesium.HermiteSpline({ *     times : times, *     points : [ *         new Cesium.Cartesian3(1235398.0, -4810983.0, 4146266.0), *         new Cesium.Cartesian3(1372574.0, -5345182.0, 4606657.0), *         new Cesium.Cartesian3(-757983.0, -5542796.0, 4514323.0), *         new Cesium.Cartesian3(-2821260.0, -5248423.0, 4021290.0), *         new Cesium.Cartesian3(-2539788.0, -4724797.0, 3620093.0) *     ], *     outTangents : [ *         new Cesium.Cartesian3(1125196, -161816, 270551), *         new Cesium.Cartesian3(-996690.5, -365906.5, 184028.5), *         new Cesium.Cartesian3(-2096917, 48379.5, -292683.5), *         new Cesium.Cartesian3(-890902.5, 408999.5, -447115) *     ], *     inTangents : [ *         new Cesium.Cartesian3(-1993381, -731813, 368057), *         new Cesium.Cartesian3(-4193834, 96759, -585367), *         new Cesium.Cartesian3(-1781805, 817999, -894230), *         new Cesium.Cartesian3(1165345, 112641, 47281) *     ] * }); * * const p0 = spline.evaluate(times[0]); * * @see ConstantSpline * @see SteppedSpline * @see LinearSpline * @see CatmullRomSpline * @see QuaternionSpline * @see MorphWeightSpline */function HermiteSpline(options) {  options = defaultValue(options, defaultValue.EMPTY_OBJECT);  const points = options.points;  const times = options.times;  const inTangents = options.inTangents;  const outTangents = options.outTangents;  //>>includeStart('debug', pragmas.debug);  if (    !defined(points) ||    !defined(times) ||    !defined(inTangents) ||    !defined(outTangents)  ) {    throw new DeveloperError(      "times, points, inTangents, and outTangents are required."    );  }  if (points.length < 2) {    throw new DeveloperError(      "points.length must be greater than or equal to 2."    );  }  if (times.length !== points.length) {    throw new DeveloperError("times.length must be equal to points.length.");  }  if (    inTangents.length !== outTangents.length ||    inTangents.length !== points.length - 1  ) {    throw new DeveloperError(      "inTangents and outTangents must have a length equal to points.length - 1."    );  }  //>>includeEnd('debug');  this._times = times;  this._points = points;  this._pointType = Spline.getPointType(points[0]);  //>>includeStart('debug', pragmas.debug);  if (    this._pointType !== Spline.getPointType(inTangents[0]) ||    this._pointType !== Spline.getPointType(outTangents[0])  ) {    throw new DeveloperError(      "inTangents and outTangents must be of the same type as points."    );  }  //>>includeEnd('debug');  this._inTangents = inTangents;  this._outTangents = outTangents;  this._lastTimeIndex = 0;}Object.defineProperties(HermiteSpline.prototype, {  /**   * An array of times for the control points.   *   * @memberof HermiteSpline.prototype   *   * @type {Number[]}   * @readonly   */  times: {    get: function () {      return this._times;    },  },  /**   * An array of control points.   *   * @memberof HermiteSpline.prototype   *   * @type {Cartesian3[]}   * @readonly   */  points: {    get: function () {      return this._points;    },  },  /**   * An array of incoming tangents at each control point.   *   * @memberof HermiteSpline.prototype   *   * @type {Cartesian3[]}   * @readonly   */  inTangents: {    get: function () {      return this._inTangents;    },  },  /**   * An array of outgoing tangents at each control point.   *   * @memberof HermiteSpline.prototype   *   * @type {Cartesian3[]}   * @readonly   */  outTangents: {    get: function () {      return this._outTangents;    },  },});/** * Creates a spline where the tangents at each control point are the same. * The curves are guaranteed to be at least in the class C<sup>1</sup>. * * @param {Object} options Object with the following properties: * @param {Number[]} options.times The array of control point times. * @param {Cartesian3[]} options.points The array of control points. * @param {Cartesian3[]} options.tangents The array of tangents at the control points. * @returns {HermiteSpline} A hermite spline. * * @exception {DeveloperError} points, times and tangents are required. * @exception {DeveloperError} points.length must be greater than or equal to 2. * @exception {DeveloperError} times, points and tangents must have the same length. * * @example * const points = [ *     new Cesium.Cartesian3(1235398.0, -4810983.0, 4146266.0), *     new Cesium.Cartesian3(1372574.0, -5345182.0, 4606657.0), *     new Cesium.Cartesian3(-757983.0, -5542796.0, 4514323.0), *     new Cesium.Cartesian3(-2821260.0, -5248423.0, 4021290.0), *     new Cesium.Cartesian3(-2539788.0, -4724797.0, 3620093.0) * ]; * * // Add tangents * const tangents = new Array(points.length); * tangents[0] = new Cesium.Cartesian3(1125196, -161816, 270551); * const temp = new Cesium.Cartesian3(); * for (let i = 1; i < tangents.length - 1; ++i) { *     tangents[i] = Cesium.Cartesian3.multiplyByScalar(Cesium.Cartesian3.subtract(points[i + 1], points[i - 1], temp), 0.5, new Cesium.Cartesian3()); * } * tangents[tangents.length - 1] = new Cesium.Cartesian3(1165345, 112641, 47281); * * const spline = Cesium.HermiteSpline.createC1({ *     times : times, *     points : points, *     tangents : tangents * }); */HermiteSpline.createC1 = function (options) {  options = defaultValue(options, defaultValue.EMPTY_OBJECT);  const times = options.times;  const points = options.points;  const tangents = options.tangents;  //>>includeStart('debug', pragmas.debug);  if (!defined(points) || !defined(times) || !defined(tangents)) {    throw new DeveloperError("points, times and tangents are required.");  }  if (points.length < 2) {    throw new DeveloperError(      "points.length must be greater than or equal to 2."    );  }  if (times.length !== points.length || times.length !== tangents.length) {    throw new DeveloperError(      "times, points and tangents must have the same length."    );  }  //>>includeEnd('debug');  const outTangents = tangents.slice(0, tangents.length - 1);  const inTangents = tangents.slice(1, tangents.length);  return new HermiteSpline({    times: times,    points: points,    inTangents: inTangents,    outTangents: outTangents,  });};/** * Creates a natural cubic spline. The tangents at the control points are generated * to create a curve in the class C<sup>2</sup>. * * @param {Object} options Object with the following properties: * @param {Number[]} options.times The array of control point times. * @param {Cartesian3[]} options.points The array of control points. * @returns {HermiteSpline|LinearSpline} A hermite spline, or a linear spline if less than 3 control points were given. * * @exception {DeveloperError} points and times are required. * @exception {DeveloperError} points.length must be greater than or equal to 2. * @exception {DeveloperError} times.length must be equal to points.length. * * @example * // Create a natural cubic spline above the earth from Philadelphia to Los Angeles. * const spline = Cesium.HermiteSpline.createNaturalCubic({ *     times : [ 0.0, 1.5, 3.0, 4.5, 6.0 ], *     points : [ *         new Cesium.Cartesian3(1235398.0, -4810983.0, 4146266.0), *         new Cesium.Cartesian3(1372574.0, -5345182.0, 4606657.0), *         new Cesium.Cartesian3(-757983.0, -5542796.0, 4514323.0), *         new Cesium.Cartesian3(-2821260.0, -5248423.0, 4021290.0), *         new Cesium.Cartesian3(-2539788.0, -4724797.0, 3620093.0) *     ] * }); */HermiteSpline.createNaturalCubic = function (options) {  options = defaultValue(options, defaultValue.EMPTY_OBJECT);  const times = options.times;  const points = options.points;  //>>includeStart('debug', pragmas.debug);  if (!defined(points) || !defined(times)) {    throw new DeveloperError("points and times are required.");  }  if (points.length < 2) {    throw new DeveloperError(      "points.length must be greater than or equal to 2."    );  }  if (times.length !== points.length) {    throw new DeveloperError("times.length must be equal to points.length.");  }  //>>includeEnd('debug');  if (points.length < 3) {    return new LinearSpline({      points: points,      times: times,    });  }  const tangents = generateNatural(points);  const outTangents = tangents.slice(0, tangents.length - 1);  const inTangents = tangents.slice(1, tangents.length);  return new HermiteSpline({    times: times,    points: points,    inTangents: inTangents,    outTangents: outTangents,  });};/** * Creates a clamped cubic spline. The tangents at the interior control points are generated * to create a curve in the class C<sup>2</sup>. * * @param {Object} options Object with the following properties: * @param {Number[]} options.times The array of control point times. * @param {Number[]|Cartesian3[]} options.points The array of control points. * @param {Cartesian3} options.firstTangent The outgoing tangent of the first control point. * @param {Cartesian3} options.lastTangent The incoming tangent of the last control point. * @returns {HermiteSpline|LinearSpline} A hermite spline, or a linear spline if less than 3 control points were given. * * @exception {DeveloperError} points, times, firstTangent and lastTangent are required. * @exception {DeveloperError} points.length must be greater than or equal to 2. * @exception {DeveloperError} times.length must be equal to points.length. * @exception {DeveloperError} firstTangent and lastTangent must be of the same type as points. * * @example * // Create a clamped cubic spline above the earth from Philadelphia to Los Angeles. * const spline = Cesium.HermiteSpline.createClampedCubic({ *     times : [ 0.0, 1.5, 3.0, 4.5, 6.0 ], *     points : [ *         new Cesium.Cartesian3(1235398.0, -4810983.0, 4146266.0), *         new Cesium.Cartesian3(1372574.0, -5345182.0, 4606657.0), *         new Cesium.Cartesian3(-757983.0, -5542796.0, 4514323.0), *         new Cesium.Cartesian3(-2821260.0, -5248423.0, 4021290.0), *         new Cesium.Cartesian3(-2539788.0, -4724797.0, 3620093.0) *     ], *     firstTangent : new Cesium.Cartesian3(1125196, -161816, 270551), *     lastTangent : new Cesium.Cartesian3(1165345, 112641, 47281) * }); */HermiteSpline.createClampedCubic = function (options) {  options = defaultValue(options, defaultValue.EMPTY_OBJECT);  const times = options.times;  const points = options.points;  const firstTangent = options.firstTangent;  const lastTangent = options.lastTangent;  //>>includeStart('debug', pragmas.debug);  if (    !defined(points) ||    !defined(times) ||    !defined(firstTangent) ||    !defined(lastTangent)  ) {    throw new DeveloperError(      "points, times, firstTangent and lastTangent are required."    );  }  if (points.length < 2) {    throw new DeveloperError(      "points.length must be greater than or equal to 2."    );  }  if (times.length !== points.length) {    throw new DeveloperError("times.length must be equal to points.length.");  }  //>>includeEnd('debug');  const PointType = Spline.getPointType(points[0]);  //>>includeStart('debug', pragmas.debug);  if (    PointType !== Spline.getPointType(firstTangent) ||    PointType !== Spline.getPointType(lastTangent)  ) {    throw new DeveloperError(      "firstTangent and lastTangent must be of the same type as points."    );  }  //>>includeEnd('debug');  if (points.length < 3) {    return new LinearSpline({      points: points,      times: times,    });  }  const tangents = generateClamped(points, firstTangent, lastTangent);  const outTangents = tangents.slice(0, tangents.length - 1);  const inTangents = tangents.slice(1, tangents.length);  return new HermiteSpline({    times: times,    points: points,    inTangents: inTangents,    outTangents: outTangents,  });};//prettier-ignoreHermiteSpline.hermiteCoefficientMatrix = new Matrix4(  2.0, -3.0, 0.0, 1.0,  -2.0, 3.0, 0.0, 0.0,  1.0, -2.0, 1.0, 0.0,  1.0, -1.0, 0.0, 0.0);/** * Finds an index <code>i</code> in <code>times</code> such that the parameter * <code>time</code> is in the interval <code>[times[i], times[i + 1]]</code>. * @function * * @param {Number} time The time. * @returns {Number} The index for the element at the start of the interval. * * @exception {DeveloperError} time must be in the range <code>[t<sub>0</sub>, t<sub>n</sub>]</code>, where <code>t<sub>0</sub></code> *                             is the first element in the array <code>times</code> and <code>t<sub>n</sub></code> is the last element *                             in the array <code>times</code>. */HermiteSpline.prototype.findTimeInterval = Spline.prototype.findTimeInterval;const scratchTimeVec = new Cartesian4();const scratchTemp = new Cartesian3();/** * Wraps the given time to the period covered by the spline. * @function * * @param {Number} time The time. * @return {Number} The time, wrapped around to the updated animation. */HermiteSpline.prototype.wrapTime = Spline.prototype.wrapTime;/** * Clamps the given time to the period covered by the spline. * @function * * @param {Number} time The time. * @return {Number} The time, clamped to the animation period. */HermiteSpline.prototype.clampTime = Spline.prototype.clampTime;/** * Evaluates the curve at a given time. * * @param {Number} time The time at which to evaluate the curve. * @param {Cartesian3} [result] The object onto which to store the result. * @returns {Cartesian3} The modified result parameter or a new instance of the point on the curve at the given time. * * @exception {DeveloperError} time must be in the range <code>[t<sub>0</sub>, t<sub>n</sub>]</code>, where <code>t<sub>0</sub></code> *                             is the first element in the array <code>times</code> and <code>t<sub>n</sub></code> is the last element *                             in the array <code>times</code>. */HermiteSpline.prototype.evaluate = function (time, result) {  const points = this.points;  const times = this.times;  const inTangents = this.inTangents;  const outTangents = this.outTangents;  this._lastTimeIndex = this.findTimeInterval(time, this._lastTimeIndex);  const i = this._lastTimeIndex;  const timesDelta = times[i + 1] - times[i];  const u = (time - times[i]) / timesDelta;  const timeVec = scratchTimeVec;  timeVec.z = u;  timeVec.y = u * u;  timeVec.x = timeVec.y * u;  timeVec.w = 1.0;  // Coefficients are returned in the following order:  // start, end, out-tangent, in-tangent  const coefs = Matrix4.multiplyByVector(    HermiteSpline.hermiteCoefficientMatrix,    timeVec,    timeVec  );  // Multiply the out-tangent and in-tangent values by the time delta.  coefs.z *= timesDelta;  coefs.w *= timesDelta;  const PointType = this._pointType;  if (PointType === Number) {    return (      points[i] * coefs.x +      points[i + 1] * coefs.y +      outTangents[i] * coefs.z +      inTangents[i] * coefs.w    );  }  if (!defined(result)) {    result = new PointType();  }  result = PointType.multiplyByScalar(points[i], coefs.x, result);  PointType.multiplyByScalar(points[i + 1], coefs.y, scratchTemp);  PointType.add(result, scratchTemp, result);  PointType.multiplyByScalar(outTangents[i], coefs.z, scratchTemp);  PointType.add(result, scratchTemp, result);  PointType.multiplyByScalar(inTangents[i], coefs.w, scratchTemp);  return PointType.add(result, scratchTemp, result);};export default HermiteSpline;
 |