HermiteSpline.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620
  1. import Cartesian3 from "./Cartesian3.js";
  2. import Cartesian4 from "./Cartesian4.js";
  3. import defaultValue from "./defaultValue.js";
  4. import defined from "./defined.js";
  5. import DeveloperError from "./DeveloperError.js";
  6. import LinearSpline from "./LinearSpline.js";
  7. import Matrix4 from "./Matrix4.js";
  8. import Spline from "./Spline.js";
  9. import TridiagonalSystemSolver from "./TridiagonalSystemSolver.js";
  10. const scratchLower = [];
  11. const scratchDiagonal = [];
  12. const scratchUpper = [];
  13. const scratchRight = [];
  14. function generateClamped(points, firstTangent, lastTangent) {
  15. const l = scratchLower;
  16. const u = scratchUpper;
  17. const d = scratchDiagonal;
  18. const r = scratchRight;
  19. l.length = u.length = points.length - 1;
  20. d.length = r.length = points.length;
  21. let i;
  22. l[0] = d[0] = 1.0;
  23. u[0] = 0.0;
  24. let right = r[0];
  25. if (!defined(right)) {
  26. right = r[0] = new Cartesian3();
  27. }
  28. Cartesian3.clone(firstTangent, right);
  29. for (i = 1; i < l.length - 1; ++i) {
  30. l[i] = u[i] = 1.0;
  31. d[i] = 4.0;
  32. right = r[i];
  33. if (!defined(right)) {
  34. right = r[i] = new Cartesian3();
  35. }
  36. Cartesian3.subtract(points[i + 1], points[i - 1], right);
  37. Cartesian3.multiplyByScalar(right, 3.0, right);
  38. }
  39. l[i] = 0.0;
  40. u[i] = 1.0;
  41. d[i] = 4.0;
  42. right = r[i];
  43. if (!defined(right)) {
  44. right = r[i] = new Cartesian3();
  45. }
  46. Cartesian3.subtract(points[i + 1], points[i - 1], right);
  47. Cartesian3.multiplyByScalar(right, 3.0, right);
  48. d[i + 1] = 1.0;
  49. right = r[i + 1];
  50. if (!defined(right)) {
  51. right = r[i + 1] = new Cartesian3();
  52. }
  53. Cartesian3.clone(lastTangent, right);
  54. return TridiagonalSystemSolver.solve(l, d, u, r);
  55. }
  56. function generateNatural(points) {
  57. const l = scratchLower;
  58. const u = scratchUpper;
  59. const d = scratchDiagonal;
  60. const r = scratchRight;
  61. l.length = u.length = points.length - 1;
  62. d.length = r.length = points.length;
  63. let i;
  64. l[0] = u[0] = 1.0;
  65. d[0] = 2.0;
  66. let right = r[0];
  67. if (!defined(right)) {
  68. right = r[0] = new Cartesian3();
  69. }
  70. Cartesian3.subtract(points[1], points[0], right);
  71. Cartesian3.multiplyByScalar(right, 3.0, right);
  72. for (i = 1; i < l.length; ++i) {
  73. l[i] = u[i] = 1.0;
  74. d[i] = 4.0;
  75. right = r[i];
  76. if (!defined(right)) {
  77. right = r[i] = new Cartesian3();
  78. }
  79. Cartesian3.subtract(points[i + 1], points[i - 1], right);
  80. Cartesian3.multiplyByScalar(right, 3.0, right);
  81. }
  82. d[i] = 2.0;
  83. right = r[i];
  84. if (!defined(right)) {
  85. right = r[i] = new Cartesian3();
  86. }
  87. Cartesian3.subtract(points[i], points[i - 1], right);
  88. Cartesian3.multiplyByScalar(right, 3.0, right);
  89. return TridiagonalSystemSolver.solve(l, d, u, r);
  90. }
  91. /**
  92. * A Hermite spline is a cubic interpolating spline. Points, incoming tangents, outgoing tangents, and times
  93. * must be defined for each control point. The outgoing tangents are defined for points [0, n - 2] and the incoming
  94. * tangents are defined for points [1, n - 1]. For example, when interpolating a segment of the curve between <code>points[i]</code> and
  95. * <code>points[i + 1]</code>, the tangents at the points will be <code>outTangents[i]</code> and <code>inTangents[i]</code>,
  96. * respectively.
  97. *
  98. * @alias HermiteSpline
  99. * @constructor
  100. *
  101. * @param {Object} options Object with the following properties:
  102. * @param {Number[]} options.times An array of strictly increasing, unit-less, floating-point times at each point.
  103. * The values are in no way connected to the clock time. They are the parameterization for the curve.
  104. * @param {Cartesian3[]} options.points The array of control points.
  105. * @param {Cartesian3[]} options.inTangents The array of incoming tangents at each control point.
  106. * @param {Cartesian3[]} options.outTangents The array of outgoing tangents at each control point.
  107. *
  108. * @exception {DeveloperError} points.length must be greater than or equal to 2.
  109. * @exception {DeveloperError} times.length must be equal to points.length.
  110. * @exception {DeveloperError} inTangents and outTangents must have a length equal to points.length - 1.
  111. * @exception {DeveloperError} inTangents and outTangents must be of the same type as points.
  112. *
  113. * @example
  114. * // Create a G<sup>1</sup> continuous Hermite spline
  115. * const times = [ 0.0, 1.5, 3.0, 4.5, 6.0 ];
  116. * const spline = new Cesium.HermiteSpline({
  117. * times : times,
  118. * points : [
  119. * new Cesium.Cartesian3(1235398.0, -4810983.0, 4146266.0),
  120. * new Cesium.Cartesian3(1372574.0, -5345182.0, 4606657.0),
  121. * new Cesium.Cartesian3(-757983.0, -5542796.0, 4514323.0),
  122. * new Cesium.Cartesian3(-2821260.0, -5248423.0, 4021290.0),
  123. * new Cesium.Cartesian3(-2539788.0, -4724797.0, 3620093.0)
  124. * ],
  125. * outTangents : [
  126. * new Cesium.Cartesian3(1125196, -161816, 270551),
  127. * new Cesium.Cartesian3(-996690.5, -365906.5, 184028.5),
  128. * new Cesium.Cartesian3(-2096917, 48379.5, -292683.5),
  129. * new Cesium.Cartesian3(-890902.5, 408999.5, -447115)
  130. * ],
  131. * inTangents : [
  132. * new Cesium.Cartesian3(-1993381, -731813, 368057),
  133. * new Cesium.Cartesian3(-4193834, 96759, -585367),
  134. * new Cesium.Cartesian3(-1781805, 817999, -894230),
  135. * new Cesium.Cartesian3(1165345, 112641, 47281)
  136. * ]
  137. * });
  138. *
  139. * const p0 = spline.evaluate(times[0]);
  140. *
  141. * @see ConstantSpline
  142. * @see SteppedSpline
  143. * @see LinearSpline
  144. * @see CatmullRomSpline
  145. * @see QuaternionSpline
  146. * @see MorphWeightSpline
  147. */
  148. function HermiteSpline(options) {
  149. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  150. const points = options.points;
  151. const times = options.times;
  152. const inTangents = options.inTangents;
  153. const outTangents = options.outTangents;
  154. //>>includeStart('debug', pragmas.debug);
  155. if (
  156. !defined(points) ||
  157. !defined(times) ||
  158. !defined(inTangents) ||
  159. !defined(outTangents)
  160. ) {
  161. throw new DeveloperError(
  162. "times, points, inTangents, and outTangents are required."
  163. );
  164. }
  165. if (points.length < 2) {
  166. throw new DeveloperError(
  167. "points.length must be greater than or equal to 2."
  168. );
  169. }
  170. if (times.length !== points.length) {
  171. throw new DeveloperError("times.length must be equal to points.length.");
  172. }
  173. if (
  174. inTangents.length !== outTangents.length ||
  175. inTangents.length !== points.length - 1
  176. ) {
  177. throw new DeveloperError(
  178. "inTangents and outTangents must have a length equal to points.length - 1."
  179. );
  180. }
  181. //>>includeEnd('debug');
  182. this._times = times;
  183. this._points = points;
  184. this._pointType = Spline.getPointType(points[0]);
  185. //>>includeStart('debug', pragmas.debug);
  186. if (
  187. this._pointType !== Spline.getPointType(inTangents[0]) ||
  188. this._pointType !== Spline.getPointType(outTangents[0])
  189. ) {
  190. throw new DeveloperError(
  191. "inTangents and outTangents must be of the same type as points."
  192. );
  193. }
  194. //>>includeEnd('debug');
  195. this._inTangents = inTangents;
  196. this._outTangents = outTangents;
  197. this._lastTimeIndex = 0;
  198. }
  199. Object.defineProperties(HermiteSpline.prototype, {
  200. /**
  201. * An array of times for the control points.
  202. *
  203. * @memberof HermiteSpline.prototype
  204. *
  205. * @type {Number[]}
  206. * @readonly
  207. */
  208. times: {
  209. get: function () {
  210. return this._times;
  211. },
  212. },
  213. /**
  214. * An array of control points.
  215. *
  216. * @memberof HermiteSpline.prototype
  217. *
  218. * @type {Cartesian3[]}
  219. * @readonly
  220. */
  221. points: {
  222. get: function () {
  223. return this._points;
  224. },
  225. },
  226. /**
  227. * An array of incoming tangents at each control point.
  228. *
  229. * @memberof HermiteSpline.prototype
  230. *
  231. * @type {Cartesian3[]}
  232. * @readonly
  233. */
  234. inTangents: {
  235. get: function () {
  236. return this._inTangents;
  237. },
  238. },
  239. /**
  240. * An array of outgoing tangents at each control point.
  241. *
  242. * @memberof HermiteSpline.prototype
  243. *
  244. * @type {Cartesian3[]}
  245. * @readonly
  246. */
  247. outTangents: {
  248. get: function () {
  249. return this._outTangents;
  250. },
  251. },
  252. });
  253. /**
  254. * Creates a spline where the tangents at each control point are the same.
  255. * The curves are guaranteed to be at least in the class C<sup>1</sup>.
  256. *
  257. * @param {Object} options Object with the following properties:
  258. * @param {Number[]} options.times The array of control point times.
  259. * @param {Cartesian3[]} options.points The array of control points.
  260. * @param {Cartesian3[]} options.tangents The array of tangents at the control points.
  261. * @returns {HermiteSpline} A hermite spline.
  262. *
  263. * @exception {DeveloperError} points, times and tangents are required.
  264. * @exception {DeveloperError} points.length must be greater than or equal to 2.
  265. * @exception {DeveloperError} times, points and tangents must have the same length.
  266. *
  267. * @example
  268. * const points = [
  269. * new Cesium.Cartesian3(1235398.0, -4810983.0, 4146266.0),
  270. * new Cesium.Cartesian3(1372574.0, -5345182.0, 4606657.0),
  271. * new Cesium.Cartesian3(-757983.0, -5542796.0, 4514323.0),
  272. * new Cesium.Cartesian3(-2821260.0, -5248423.0, 4021290.0),
  273. * new Cesium.Cartesian3(-2539788.0, -4724797.0, 3620093.0)
  274. * ];
  275. *
  276. * // Add tangents
  277. * const tangents = new Array(points.length);
  278. * tangents[0] = new Cesium.Cartesian3(1125196, -161816, 270551);
  279. * const temp = new Cesium.Cartesian3();
  280. * for (let i = 1; i < tangents.length - 1; ++i) {
  281. * tangents[i] = Cesium.Cartesian3.multiplyByScalar(Cesium.Cartesian3.subtract(points[i + 1], points[i - 1], temp), 0.5, new Cesium.Cartesian3());
  282. * }
  283. * tangents[tangents.length - 1] = new Cesium.Cartesian3(1165345, 112641, 47281);
  284. *
  285. * const spline = Cesium.HermiteSpline.createC1({
  286. * times : times,
  287. * points : points,
  288. * tangents : tangents
  289. * });
  290. */
  291. HermiteSpline.createC1 = function (options) {
  292. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  293. const times = options.times;
  294. const points = options.points;
  295. const tangents = options.tangents;
  296. //>>includeStart('debug', pragmas.debug);
  297. if (!defined(points) || !defined(times) || !defined(tangents)) {
  298. throw new DeveloperError("points, times and tangents are required.");
  299. }
  300. if (points.length < 2) {
  301. throw new DeveloperError(
  302. "points.length must be greater than or equal to 2."
  303. );
  304. }
  305. if (times.length !== points.length || times.length !== tangents.length) {
  306. throw new DeveloperError(
  307. "times, points and tangents must have the same length."
  308. );
  309. }
  310. //>>includeEnd('debug');
  311. const outTangents = tangents.slice(0, tangents.length - 1);
  312. const inTangents = tangents.slice(1, tangents.length);
  313. return new HermiteSpline({
  314. times: times,
  315. points: points,
  316. inTangents: inTangents,
  317. outTangents: outTangents,
  318. });
  319. };
  320. /**
  321. * Creates a natural cubic spline. The tangents at the control points are generated
  322. * to create a curve in the class C<sup>2</sup>.
  323. *
  324. * @param {Object} options Object with the following properties:
  325. * @param {Number[]} options.times The array of control point times.
  326. * @param {Cartesian3[]} options.points The array of control points.
  327. * @returns {HermiteSpline|LinearSpline} A hermite spline, or a linear spline if less than 3 control points were given.
  328. *
  329. * @exception {DeveloperError} points and times are required.
  330. * @exception {DeveloperError} points.length must be greater than or equal to 2.
  331. * @exception {DeveloperError} times.length must be equal to points.length.
  332. *
  333. * @example
  334. * // Create a natural cubic spline above the earth from Philadelphia to Los Angeles.
  335. * const spline = Cesium.HermiteSpline.createNaturalCubic({
  336. * times : [ 0.0, 1.5, 3.0, 4.5, 6.0 ],
  337. * points : [
  338. * new Cesium.Cartesian3(1235398.0, -4810983.0, 4146266.0),
  339. * new Cesium.Cartesian3(1372574.0, -5345182.0, 4606657.0),
  340. * new Cesium.Cartesian3(-757983.0, -5542796.0, 4514323.0),
  341. * new Cesium.Cartesian3(-2821260.0, -5248423.0, 4021290.0),
  342. * new Cesium.Cartesian3(-2539788.0, -4724797.0, 3620093.0)
  343. * ]
  344. * });
  345. */
  346. HermiteSpline.createNaturalCubic = function (options) {
  347. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  348. const times = options.times;
  349. const points = options.points;
  350. //>>includeStart('debug', pragmas.debug);
  351. if (!defined(points) || !defined(times)) {
  352. throw new DeveloperError("points and times are required.");
  353. }
  354. if (points.length < 2) {
  355. throw new DeveloperError(
  356. "points.length must be greater than or equal to 2."
  357. );
  358. }
  359. if (times.length !== points.length) {
  360. throw new DeveloperError("times.length must be equal to points.length.");
  361. }
  362. //>>includeEnd('debug');
  363. if (points.length < 3) {
  364. return new LinearSpline({
  365. points: points,
  366. times: times,
  367. });
  368. }
  369. const tangents = generateNatural(points);
  370. const outTangents = tangents.slice(0, tangents.length - 1);
  371. const inTangents = tangents.slice(1, tangents.length);
  372. return new HermiteSpline({
  373. times: times,
  374. points: points,
  375. inTangents: inTangents,
  376. outTangents: outTangents,
  377. });
  378. };
  379. /**
  380. * Creates a clamped cubic spline. The tangents at the interior control points are generated
  381. * to create a curve in the class C<sup>2</sup>.
  382. *
  383. * @param {Object} options Object with the following properties:
  384. * @param {Number[]} options.times The array of control point times.
  385. * @param {Number[]|Cartesian3[]} options.points The array of control points.
  386. * @param {Cartesian3} options.firstTangent The outgoing tangent of the first control point.
  387. * @param {Cartesian3} options.lastTangent The incoming tangent of the last control point.
  388. * @returns {HermiteSpline|LinearSpline} A hermite spline, or a linear spline if less than 3 control points were given.
  389. *
  390. * @exception {DeveloperError} points, times, firstTangent and lastTangent are required.
  391. * @exception {DeveloperError} points.length must be greater than or equal to 2.
  392. * @exception {DeveloperError} times.length must be equal to points.length.
  393. * @exception {DeveloperError} firstTangent and lastTangent must be of the same type as points.
  394. *
  395. * @example
  396. * // Create a clamped cubic spline above the earth from Philadelphia to Los Angeles.
  397. * const spline = Cesium.HermiteSpline.createClampedCubic({
  398. * times : [ 0.0, 1.5, 3.0, 4.5, 6.0 ],
  399. * points : [
  400. * new Cesium.Cartesian3(1235398.0, -4810983.0, 4146266.0),
  401. * new Cesium.Cartesian3(1372574.0, -5345182.0, 4606657.0),
  402. * new Cesium.Cartesian3(-757983.0, -5542796.0, 4514323.0),
  403. * new Cesium.Cartesian3(-2821260.0, -5248423.0, 4021290.0),
  404. * new Cesium.Cartesian3(-2539788.0, -4724797.0, 3620093.0)
  405. * ],
  406. * firstTangent : new Cesium.Cartesian3(1125196, -161816, 270551),
  407. * lastTangent : new Cesium.Cartesian3(1165345, 112641, 47281)
  408. * });
  409. */
  410. HermiteSpline.createClampedCubic = function (options) {
  411. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  412. const times = options.times;
  413. const points = options.points;
  414. const firstTangent = options.firstTangent;
  415. const lastTangent = options.lastTangent;
  416. //>>includeStart('debug', pragmas.debug);
  417. if (
  418. !defined(points) ||
  419. !defined(times) ||
  420. !defined(firstTangent) ||
  421. !defined(lastTangent)
  422. ) {
  423. throw new DeveloperError(
  424. "points, times, firstTangent and lastTangent are required."
  425. );
  426. }
  427. if (points.length < 2) {
  428. throw new DeveloperError(
  429. "points.length must be greater than or equal to 2."
  430. );
  431. }
  432. if (times.length !== points.length) {
  433. throw new DeveloperError("times.length must be equal to points.length.");
  434. }
  435. //>>includeEnd('debug');
  436. const PointType = Spline.getPointType(points[0]);
  437. //>>includeStart('debug', pragmas.debug);
  438. if (
  439. PointType !== Spline.getPointType(firstTangent) ||
  440. PointType !== Spline.getPointType(lastTangent)
  441. ) {
  442. throw new DeveloperError(
  443. "firstTangent and lastTangent must be of the same type as points."
  444. );
  445. }
  446. //>>includeEnd('debug');
  447. if (points.length < 3) {
  448. return new LinearSpline({
  449. points: points,
  450. times: times,
  451. });
  452. }
  453. const tangents = generateClamped(points, firstTangent, lastTangent);
  454. const outTangents = tangents.slice(0, tangents.length - 1);
  455. const inTangents = tangents.slice(1, tangents.length);
  456. return new HermiteSpline({
  457. times: times,
  458. points: points,
  459. inTangents: inTangents,
  460. outTangents: outTangents,
  461. });
  462. };
  463. //prettier-ignore
  464. HermiteSpline.hermiteCoefficientMatrix = new Matrix4(
  465. 2.0, -3.0, 0.0, 1.0,
  466. -2.0, 3.0, 0.0, 0.0,
  467. 1.0, -2.0, 1.0, 0.0,
  468. 1.0, -1.0, 0.0, 0.0
  469. );
  470. /**
  471. * Finds an index <code>i</code> in <code>times</code> such that the parameter
  472. * <code>time</code> is in the interval <code>[times[i], times[i + 1]]</code>.
  473. * @function
  474. *
  475. * @param {Number} time The time.
  476. * @returns {Number} The index for the element at the start of the interval.
  477. *
  478. * @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>
  479. * is the first element in the array <code>times</code> and <code>t<sub>n</sub></code> is the last element
  480. * in the array <code>times</code>.
  481. */
  482. HermiteSpline.prototype.findTimeInterval = Spline.prototype.findTimeInterval;
  483. const scratchTimeVec = new Cartesian4();
  484. const scratchTemp = new Cartesian3();
  485. /**
  486. * Wraps the given time to the period covered by the spline.
  487. * @function
  488. *
  489. * @param {Number} time The time.
  490. * @return {Number} The time, wrapped around to the updated animation.
  491. */
  492. HermiteSpline.prototype.wrapTime = Spline.prototype.wrapTime;
  493. /**
  494. * Clamps the given time to the period covered by the spline.
  495. * @function
  496. *
  497. * @param {Number} time The time.
  498. * @return {Number} The time, clamped to the animation period.
  499. */
  500. HermiteSpline.prototype.clampTime = Spline.prototype.clampTime;
  501. /**
  502. * Evaluates the curve at a given time.
  503. *
  504. * @param {Number} time The time at which to evaluate the curve.
  505. * @param {Cartesian3} [result] The object onto which to store the result.
  506. * @returns {Cartesian3} The modified result parameter or a new instance of the point on the curve at the given time.
  507. *
  508. * @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>
  509. * is the first element in the array <code>times</code> and <code>t<sub>n</sub></code> is the last element
  510. * in the array <code>times</code>.
  511. */
  512. HermiteSpline.prototype.evaluate = function (time, result) {
  513. const points = this.points;
  514. const times = this.times;
  515. const inTangents = this.inTangents;
  516. const outTangents = this.outTangents;
  517. this._lastTimeIndex = this.findTimeInterval(time, this._lastTimeIndex);
  518. const i = this._lastTimeIndex;
  519. const timesDelta = times[i + 1] - times[i];
  520. const u = (time - times[i]) / timesDelta;
  521. const timeVec = scratchTimeVec;
  522. timeVec.z = u;
  523. timeVec.y = u * u;
  524. timeVec.x = timeVec.y * u;
  525. timeVec.w = 1.0;
  526. // Coefficients are returned in the following order:
  527. // start, end, out-tangent, in-tangent
  528. const coefs = Matrix4.multiplyByVector(
  529. HermiteSpline.hermiteCoefficientMatrix,
  530. timeVec,
  531. timeVec
  532. );
  533. // Multiply the out-tangent and in-tangent values by the time delta.
  534. coefs.z *= timesDelta;
  535. coefs.w *= timesDelta;
  536. const PointType = this._pointType;
  537. if (PointType === Number) {
  538. return (
  539. points[i] * coefs.x +
  540. points[i + 1] * coefs.y +
  541. outTangents[i] * coefs.z +
  542. inTangents[i] * coefs.w
  543. );
  544. }
  545. if (!defined(result)) {
  546. result = new PointType();
  547. }
  548. result = PointType.multiplyByScalar(points[i], coefs.x, result);
  549. PointType.multiplyByScalar(points[i + 1], coefs.y, scratchTemp);
  550. PointType.add(result, scratchTemp, result);
  551. PointType.multiplyByScalar(outTangents[i], coefs.z, scratchTemp);
  552. PointType.add(result, scratchTemp, result);
  553. PointType.multiplyByScalar(inTangents[i], coefs.w, scratchTemp);
  554. return PointType.add(result, scratchTemp, result);
  555. };
  556. export default HermiteSpline;