SampledProperty.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834
  1. import binarySearch from "../Core/binarySearch.js";
  2. import Check from "../Core/Check.js";
  3. import defaultValue from "../Core/defaultValue.js";
  4. import defined from "../Core/defined.js";
  5. import DeveloperError from "../Core/DeveloperError.js";
  6. import Event from "../Core/Event.js";
  7. import ExtrapolationType from "../Core/ExtrapolationType.js";
  8. import JulianDate from "../Core/JulianDate.js";
  9. import LinearApproximation from "../Core/LinearApproximation.js";
  10. const PackableNumber = {
  11. packedLength: 1,
  12. pack: function (value, array, startingIndex) {
  13. startingIndex = defaultValue(startingIndex, 0);
  14. array[startingIndex] = value;
  15. },
  16. unpack: function (array, startingIndex, result) {
  17. startingIndex = defaultValue(startingIndex, 0);
  18. return array[startingIndex];
  19. },
  20. };
  21. //We can't use splice for inserting new elements because function apply can't handle
  22. //a huge number of arguments. See https://code.google.com/p/chromium/issues/detail?id=56588
  23. function arrayInsert(array, startIndex, items) {
  24. let i;
  25. const arrayLength = array.length;
  26. const itemsLength = items.length;
  27. const newLength = arrayLength + itemsLength;
  28. array.length = newLength;
  29. if (arrayLength !== startIndex) {
  30. let q = arrayLength - 1;
  31. for (i = newLength - 1; i >= startIndex; i--) {
  32. array[i] = array[q--];
  33. }
  34. }
  35. for (i = 0; i < itemsLength; i++) {
  36. array[startIndex++] = items[i];
  37. }
  38. }
  39. function convertDate(date, epoch) {
  40. if (date instanceof JulianDate) {
  41. return date;
  42. }
  43. if (typeof date === "string") {
  44. return JulianDate.fromIso8601(date);
  45. }
  46. return JulianDate.addSeconds(epoch, date, new JulianDate());
  47. }
  48. const timesSpliceArgs = [];
  49. const valuesSpliceArgs = [];
  50. function mergeNewSamples(epoch, times, values, newData, packedLength) {
  51. let newDataIndex = 0;
  52. let i;
  53. let prevItem;
  54. let timesInsertionPoint;
  55. let valuesInsertionPoint;
  56. let currentTime;
  57. let nextTime;
  58. while (newDataIndex < newData.length) {
  59. currentTime = convertDate(newData[newDataIndex], epoch);
  60. timesInsertionPoint = binarySearch(times, currentTime, JulianDate.compare);
  61. let timesSpliceArgsCount = 0;
  62. let valuesSpliceArgsCount = 0;
  63. if (timesInsertionPoint < 0) {
  64. //Doesn't exist, insert as many additional values as we can.
  65. timesInsertionPoint = ~timesInsertionPoint;
  66. valuesInsertionPoint = timesInsertionPoint * packedLength;
  67. prevItem = undefined;
  68. nextTime = times[timesInsertionPoint];
  69. while (newDataIndex < newData.length) {
  70. currentTime = convertDate(newData[newDataIndex], epoch);
  71. if (
  72. (defined(prevItem) &&
  73. JulianDate.compare(prevItem, currentTime) >= 0) ||
  74. (defined(nextTime) && JulianDate.compare(currentTime, nextTime) >= 0)
  75. ) {
  76. break;
  77. }
  78. timesSpliceArgs[timesSpliceArgsCount++] = currentTime;
  79. newDataIndex = newDataIndex + 1;
  80. for (i = 0; i < packedLength; i++) {
  81. valuesSpliceArgs[valuesSpliceArgsCount++] = newData[newDataIndex];
  82. newDataIndex = newDataIndex + 1;
  83. }
  84. prevItem = currentTime;
  85. }
  86. if (timesSpliceArgsCount > 0) {
  87. valuesSpliceArgs.length = valuesSpliceArgsCount;
  88. arrayInsert(values, valuesInsertionPoint, valuesSpliceArgs);
  89. timesSpliceArgs.length = timesSpliceArgsCount;
  90. arrayInsert(times, timesInsertionPoint, timesSpliceArgs);
  91. }
  92. } else {
  93. //Found an exact match
  94. for (i = 0; i < packedLength; i++) {
  95. newDataIndex++;
  96. values[timesInsertionPoint * packedLength + i] = newData[newDataIndex];
  97. }
  98. newDataIndex++;
  99. }
  100. }
  101. }
  102. /**
  103. * A {@link Property} whose value is interpolated for a given time from the
  104. * provided set of samples and specified interpolation algorithm and degree.
  105. * @alias SampledProperty
  106. * @constructor
  107. *
  108. * @param {number|Packable} type The type of property.
  109. * @param {Packable[]} [derivativeTypes] When supplied, indicates that samples will contain derivative information of the specified types.
  110. *
  111. *
  112. * @example
  113. * //Create a linearly interpolated Cartesian2
  114. * const property = new Cesium.SampledProperty(Cesium.Cartesian2);
  115. *
  116. * //Populate it with data
  117. * property.addSample(Cesium.JulianDate.fromIso8601('2012-08-01T00:00:00.00Z'), new Cesium.Cartesian2(0, 0));
  118. * property.addSample(Cesium.JulianDate.fromIso8601('2012-08-02T00:00:00.00Z'), new Cesium.Cartesian2(4, 7));
  119. *
  120. * //Retrieve an interpolated value
  121. * const result = property.getValue(Cesium.JulianDate.fromIso8601('2012-08-01T12:00:00.00Z'));
  122. *
  123. * @example
  124. * //Create a simple numeric SampledProperty that uses third degree Hermite Polynomial Approximation
  125. * const property = new Cesium.SampledProperty(Number);
  126. * property.setInterpolationOptions({
  127. * interpolationDegree : 3,
  128. * interpolationAlgorithm : Cesium.HermitePolynomialApproximation
  129. * });
  130. *
  131. * //Populate it with data
  132. * property.addSample(Cesium.JulianDate.fromIso8601('2012-08-01T00:00:00.00Z'), 1.0);
  133. * property.addSample(Cesium.JulianDate.fromIso8601('2012-08-01T00:01:00.00Z'), 6.0);
  134. * property.addSample(Cesium.JulianDate.fromIso8601('2012-08-01T00:02:00.00Z'), 12.0);
  135. * property.addSample(Cesium.JulianDate.fromIso8601('2012-08-01T00:03:30.00Z'), 5.0);
  136. * property.addSample(Cesium.JulianDate.fromIso8601('2012-08-01T00:06:30.00Z'), 2.0);
  137. *
  138. * //Samples can be added in any order.
  139. * property.addSample(Cesium.JulianDate.fromIso8601('2012-08-01T00:00:30.00Z'), 6.2);
  140. *
  141. * //Retrieve an interpolated value
  142. * const result = property.getValue(Cesium.JulianDate.fromIso8601('2012-08-01T00:02:34.00Z'));
  143. *
  144. * @see SampledPositionProperty
  145. */
  146. function SampledProperty(type, derivativeTypes) {
  147. //>>includeStart('debug', pragmas.debug);
  148. Check.defined("type", type);
  149. //>>includeEnd('debug');
  150. let innerType = type;
  151. if (innerType === Number) {
  152. innerType = PackableNumber;
  153. }
  154. let packedLength = innerType.packedLength;
  155. let packedInterpolationLength = defaultValue(
  156. innerType.packedInterpolationLength,
  157. packedLength
  158. );
  159. let inputOrder = 0;
  160. let innerDerivativeTypes;
  161. if (defined(derivativeTypes)) {
  162. const length = derivativeTypes.length;
  163. innerDerivativeTypes = new Array(length);
  164. for (let i = 0; i < length; i++) {
  165. let derivativeType = derivativeTypes[i];
  166. if (derivativeType === Number) {
  167. derivativeType = PackableNumber;
  168. }
  169. const derivativePackedLength = derivativeType.packedLength;
  170. packedLength += derivativePackedLength;
  171. packedInterpolationLength += defaultValue(
  172. derivativeType.packedInterpolationLength,
  173. derivativePackedLength
  174. );
  175. innerDerivativeTypes[i] = derivativeType;
  176. }
  177. inputOrder = length;
  178. }
  179. this._type = type;
  180. this._innerType = innerType;
  181. this._interpolationDegree = 1;
  182. this._interpolationAlgorithm = LinearApproximation;
  183. this._numberOfPoints = 0;
  184. this._times = [];
  185. this._values = [];
  186. this._xTable = [];
  187. this._yTable = [];
  188. this._packedLength = packedLength;
  189. this._packedInterpolationLength = packedInterpolationLength;
  190. this._updateTableLength = true;
  191. this._interpolationResult = new Array(packedInterpolationLength);
  192. this._definitionChanged = new Event();
  193. this._derivativeTypes = derivativeTypes;
  194. this._innerDerivativeTypes = innerDerivativeTypes;
  195. this._inputOrder = inputOrder;
  196. this._forwardExtrapolationType = ExtrapolationType.NONE;
  197. this._forwardExtrapolationDuration = 0;
  198. this._backwardExtrapolationType = ExtrapolationType.NONE;
  199. this._backwardExtrapolationDuration = 0;
  200. }
  201. Object.defineProperties(SampledProperty.prototype, {
  202. /**
  203. * Gets a value indicating if this property is constant. A property is considered
  204. * constant if getValue always returns the same result for the current definition.
  205. * @memberof SampledProperty.prototype
  206. *
  207. * @type {boolean}
  208. * @readonly
  209. */
  210. isConstant: {
  211. get: function () {
  212. return this._values.length === 0;
  213. },
  214. },
  215. /**
  216. * Gets the event that is raised whenever the definition of this property changes.
  217. * The definition is considered to have changed if a call to getValue would return
  218. * a different result for the same time.
  219. * @memberof SampledProperty.prototype
  220. *
  221. * @type {Event}
  222. * @readonly
  223. */
  224. definitionChanged: {
  225. get: function () {
  226. return this._definitionChanged;
  227. },
  228. },
  229. /**
  230. * Gets the type of property.
  231. * @memberof SampledProperty.prototype
  232. * @type {*}
  233. */
  234. type: {
  235. get: function () {
  236. return this._type;
  237. },
  238. },
  239. /**
  240. * Gets the derivative types used by this property.
  241. * @memberof SampledProperty.prototype
  242. * @type {Packable[]}
  243. */
  244. derivativeTypes: {
  245. get: function () {
  246. return this._derivativeTypes;
  247. },
  248. },
  249. /**
  250. * Gets the degree of interpolation to perform when retrieving a value.
  251. * @memberof SampledProperty.prototype
  252. * @type {number}
  253. * @default 1
  254. */
  255. interpolationDegree: {
  256. get: function () {
  257. return this._interpolationDegree;
  258. },
  259. },
  260. /**
  261. * Gets the interpolation algorithm to use when retrieving a value.
  262. * @memberof SampledProperty.prototype
  263. * @type {InterpolationAlgorithm}
  264. * @default LinearApproximation
  265. */
  266. interpolationAlgorithm: {
  267. get: function () {
  268. return this._interpolationAlgorithm;
  269. },
  270. },
  271. /**
  272. * Gets or sets the type of extrapolation to perform when a value
  273. * is requested at a time after any available samples.
  274. * @memberof SampledProperty.prototype
  275. * @type {ExtrapolationType}
  276. * @default ExtrapolationType.NONE
  277. */
  278. forwardExtrapolationType: {
  279. get: function () {
  280. return this._forwardExtrapolationType;
  281. },
  282. set: function (value) {
  283. if (this._forwardExtrapolationType !== value) {
  284. this._forwardExtrapolationType = value;
  285. this._definitionChanged.raiseEvent(this);
  286. }
  287. },
  288. },
  289. /**
  290. * Gets or sets the amount of time to extrapolate forward before
  291. * the property becomes undefined. A value of 0 will extrapolate forever.
  292. * @memberof SampledProperty.prototype
  293. * @type {number}
  294. * @default 0
  295. */
  296. forwardExtrapolationDuration: {
  297. get: function () {
  298. return this._forwardExtrapolationDuration;
  299. },
  300. set: function (value) {
  301. if (this._forwardExtrapolationDuration !== value) {
  302. this._forwardExtrapolationDuration = value;
  303. this._definitionChanged.raiseEvent(this);
  304. }
  305. },
  306. },
  307. /**
  308. * Gets or sets the type of extrapolation to perform when a value
  309. * is requested at a time before any available samples.
  310. * @memberof SampledProperty.prototype
  311. * @type {ExtrapolationType}
  312. * @default ExtrapolationType.NONE
  313. */
  314. backwardExtrapolationType: {
  315. get: function () {
  316. return this._backwardExtrapolationType;
  317. },
  318. set: function (value) {
  319. if (this._backwardExtrapolationType !== value) {
  320. this._backwardExtrapolationType = value;
  321. this._definitionChanged.raiseEvent(this);
  322. }
  323. },
  324. },
  325. /**
  326. * Gets or sets the amount of time to extrapolate backward
  327. * before the property becomes undefined. A value of 0 will extrapolate forever.
  328. * @memberof SampledProperty.prototype
  329. * @type {number}
  330. * @default 0
  331. */
  332. backwardExtrapolationDuration: {
  333. get: function () {
  334. return this._backwardExtrapolationDuration;
  335. },
  336. set: function (value) {
  337. if (this._backwardExtrapolationDuration !== value) {
  338. this._backwardExtrapolationDuration = value;
  339. this._definitionChanged.raiseEvent(this);
  340. }
  341. },
  342. },
  343. });
  344. /**
  345. * Gets the value of the property at the provided time.
  346. *
  347. * @param {JulianDate} time The time for which to retrieve the value.
  348. * @param {object} [result] The object to store the value into, if omitted, a new instance is created and returned.
  349. * @returns {object} The modified result parameter or a new instance if the result parameter was not supplied.
  350. */
  351. SampledProperty.prototype.getValue = function (time, result) {
  352. //>>includeStart('debug', pragmas.debug);
  353. Check.defined("time", time);
  354. //>>includeEnd('debug');
  355. const times = this._times;
  356. const timesLength = times.length;
  357. if (timesLength === 0) {
  358. return undefined;
  359. }
  360. let timeout;
  361. const innerType = this._innerType;
  362. const values = this._values;
  363. let index = binarySearch(times, time, JulianDate.compare);
  364. if (index < 0) {
  365. index = ~index;
  366. if (index === 0) {
  367. const startTime = times[index];
  368. timeout = this._backwardExtrapolationDuration;
  369. if (
  370. this._backwardExtrapolationType === ExtrapolationType.NONE ||
  371. (timeout !== 0 &&
  372. JulianDate.secondsDifference(startTime, time) > timeout)
  373. ) {
  374. return undefined;
  375. }
  376. if (this._backwardExtrapolationType === ExtrapolationType.HOLD) {
  377. return innerType.unpack(values, 0, result);
  378. }
  379. }
  380. if (index >= timesLength) {
  381. index = timesLength - 1;
  382. const endTime = times[index];
  383. timeout = this._forwardExtrapolationDuration;
  384. if (
  385. this._forwardExtrapolationType === ExtrapolationType.NONE ||
  386. (timeout !== 0 && JulianDate.secondsDifference(time, endTime) > timeout)
  387. ) {
  388. return undefined;
  389. }
  390. if (this._forwardExtrapolationType === ExtrapolationType.HOLD) {
  391. index = timesLength - 1;
  392. return innerType.unpack(values, index * innerType.packedLength, result);
  393. }
  394. }
  395. const xTable = this._xTable;
  396. const yTable = this._yTable;
  397. const interpolationAlgorithm = this._interpolationAlgorithm;
  398. const packedInterpolationLength = this._packedInterpolationLength;
  399. const inputOrder = this._inputOrder;
  400. if (this._updateTableLength) {
  401. this._updateTableLength = false;
  402. const numberOfPoints = Math.min(
  403. interpolationAlgorithm.getRequiredDataPoints(
  404. this._interpolationDegree,
  405. inputOrder
  406. ),
  407. timesLength
  408. );
  409. if (numberOfPoints !== this._numberOfPoints) {
  410. this._numberOfPoints = numberOfPoints;
  411. xTable.length = numberOfPoints;
  412. yTable.length = numberOfPoints * packedInterpolationLength;
  413. }
  414. }
  415. const degree = this._numberOfPoints - 1;
  416. if (degree < 1) {
  417. return undefined;
  418. }
  419. let firstIndex = 0;
  420. let lastIndex = timesLength - 1;
  421. const pointsInCollection = lastIndex - firstIndex + 1;
  422. if (pointsInCollection >= degree + 1) {
  423. let computedFirstIndex = index - ((degree / 2) | 0) - 1;
  424. if (computedFirstIndex < firstIndex) {
  425. computedFirstIndex = firstIndex;
  426. }
  427. let computedLastIndex = computedFirstIndex + degree;
  428. if (computedLastIndex > lastIndex) {
  429. computedLastIndex = lastIndex;
  430. computedFirstIndex = computedLastIndex - degree;
  431. if (computedFirstIndex < firstIndex) {
  432. computedFirstIndex = firstIndex;
  433. }
  434. }
  435. firstIndex = computedFirstIndex;
  436. lastIndex = computedLastIndex;
  437. }
  438. const length = lastIndex - firstIndex + 1;
  439. // Build the tables
  440. for (let i = 0; i < length; ++i) {
  441. xTable[i] = JulianDate.secondsDifference(
  442. times[firstIndex + i],
  443. times[lastIndex]
  444. );
  445. }
  446. if (!defined(innerType.convertPackedArrayForInterpolation)) {
  447. let destinationIndex = 0;
  448. const packedLength = this._packedLength;
  449. let sourceIndex = firstIndex * packedLength;
  450. const stop = (lastIndex + 1) * packedLength;
  451. while (sourceIndex < stop) {
  452. yTable[destinationIndex] = values[sourceIndex];
  453. sourceIndex++;
  454. destinationIndex++;
  455. }
  456. } else {
  457. innerType.convertPackedArrayForInterpolation(
  458. values,
  459. firstIndex,
  460. lastIndex,
  461. yTable
  462. );
  463. }
  464. // Interpolate!
  465. const x = JulianDate.secondsDifference(time, times[lastIndex]);
  466. let interpolationResult;
  467. if (inputOrder === 0 || !defined(interpolationAlgorithm.interpolate)) {
  468. interpolationResult = interpolationAlgorithm.interpolateOrderZero(
  469. x,
  470. xTable,
  471. yTable,
  472. packedInterpolationLength,
  473. this._interpolationResult
  474. );
  475. } else {
  476. const yStride = Math.floor(packedInterpolationLength / (inputOrder + 1));
  477. interpolationResult = interpolationAlgorithm.interpolate(
  478. x,
  479. xTable,
  480. yTable,
  481. yStride,
  482. inputOrder,
  483. inputOrder,
  484. this._interpolationResult
  485. );
  486. }
  487. if (!defined(innerType.unpackInterpolationResult)) {
  488. return innerType.unpack(interpolationResult, 0, result);
  489. }
  490. return innerType.unpackInterpolationResult(
  491. interpolationResult,
  492. values,
  493. firstIndex,
  494. lastIndex,
  495. result
  496. );
  497. }
  498. return innerType.unpack(values, index * this._packedLength, result);
  499. };
  500. /**
  501. * Sets the algorithm and degree to use when interpolating a value.
  502. *
  503. * @param {object} [options] Object with the following properties:
  504. * @param {InterpolationAlgorithm} [options.interpolationAlgorithm] The new interpolation algorithm. If undefined, the existing property will be unchanged.
  505. * @param {number} [options.interpolationDegree] The new interpolation degree. If undefined, the existing property will be unchanged.
  506. */
  507. SampledProperty.prototype.setInterpolationOptions = function (options) {
  508. if (!defined(options)) {
  509. return;
  510. }
  511. let valuesChanged = false;
  512. const interpolationAlgorithm = options.interpolationAlgorithm;
  513. const interpolationDegree = options.interpolationDegree;
  514. if (
  515. defined(interpolationAlgorithm) &&
  516. this._interpolationAlgorithm !== interpolationAlgorithm
  517. ) {
  518. this._interpolationAlgorithm = interpolationAlgorithm;
  519. valuesChanged = true;
  520. }
  521. if (
  522. defined(interpolationDegree) &&
  523. this._interpolationDegree !== interpolationDegree
  524. ) {
  525. this._interpolationDegree = interpolationDegree;
  526. valuesChanged = true;
  527. }
  528. if (valuesChanged) {
  529. this._updateTableLength = true;
  530. this._definitionChanged.raiseEvent(this);
  531. }
  532. };
  533. /**
  534. * Adds a new sample.
  535. *
  536. * @param {JulianDate} time The sample time.
  537. * @param {Packable} value The value at the provided time.
  538. * @param {Packable[]} [derivatives] The array of derivatives at the provided time.
  539. */
  540. SampledProperty.prototype.addSample = function (time, value, derivatives) {
  541. const innerDerivativeTypes = this._innerDerivativeTypes;
  542. const hasDerivatives = defined(innerDerivativeTypes);
  543. //>>includeStart('debug', pragmas.debug);
  544. Check.defined("time", time);
  545. Check.defined("value", value);
  546. if (hasDerivatives) {
  547. Check.defined("derivatives", derivatives);
  548. }
  549. //>>includeEnd('debug');
  550. const innerType = this._innerType;
  551. const data = [];
  552. data.push(time);
  553. innerType.pack(value, data, data.length);
  554. if (hasDerivatives) {
  555. const derivativesLength = innerDerivativeTypes.length;
  556. for (let x = 0; x < derivativesLength; x++) {
  557. innerDerivativeTypes[x].pack(derivatives[x], data, data.length);
  558. }
  559. }
  560. mergeNewSamples(
  561. undefined,
  562. this._times,
  563. this._values,
  564. data,
  565. this._packedLength
  566. );
  567. this._updateTableLength = true;
  568. this._definitionChanged.raiseEvent(this);
  569. };
  570. /**
  571. * Adds an array of samples.
  572. *
  573. * @param {JulianDate[]} times An array of JulianDate instances where each index is a sample time.
  574. * @param {Packable[]} values The array of values, where each value corresponds to the provided times index.
  575. * @param {Array[]} [derivativeValues] An array where each item is the array of derivatives at the equivalent time index.
  576. *
  577. * @exception {DeveloperError} times and values must be the same length.
  578. * @exception {DeveloperError} times and derivativeValues must be the same length.
  579. */
  580. SampledProperty.prototype.addSamples = function (
  581. times,
  582. values,
  583. derivativeValues
  584. ) {
  585. const innerDerivativeTypes = this._innerDerivativeTypes;
  586. const hasDerivatives = defined(innerDerivativeTypes);
  587. //>>includeStart('debug', pragmas.debug);
  588. Check.defined("times", times);
  589. Check.defined("values", values);
  590. if (times.length !== values.length) {
  591. throw new DeveloperError("times and values must be the same length.");
  592. }
  593. if (
  594. hasDerivatives &&
  595. (!defined(derivativeValues) || derivativeValues.length !== times.length)
  596. ) {
  597. throw new DeveloperError(
  598. "times and derivativeValues must be the same length."
  599. );
  600. }
  601. //>>includeEnd('debug');
  602. const innerType = this._innerType;
  603. const length = times.length;
  604. const data = [];
  605. for (let i = 0; i < length; i++) {
  606. data.push(times[i]);
  607. innerType.pack(values[i], data, data.length);
  608. if (hasDerivatives) {
  609. const derivatives = derivativeValues[i];
  610. const derivativesLength = innerDerivativeTypes.length;
  611. for (let x = 0; x < derivativesLength; x++) {
  612. innerDerivativeTypes[x].pack(derivatives[x], data, data.length);
  613. }
  614. }
  615. }
  616. mergeNewSamples(
  617. undefined,
  618. this._times,
  619. this._values,
  620. data,
  621. this._packedLength
  622. );
  623. this._updateTableLength = true;
  624. this._definitionChanged.raiseEvent(this);
  625. };
  626. /**
  627. * Adds samples as a single packed array where each new sample is represented as a date,
  628. * followed by the packed representation of the corresponding value and derivatives.
  629. *
  630. * @param {number[]} packedSamples The array of packed samples.
  631. * @param {JulianDate} [epoch] If any of the dates in packedSamples are numbers, they are considered an offset from this epoch, in seconds.
  632. */
  633. SampledProperty.prototype.addSamplesPackedArray = function (
  634. packedSamples,
  635. epoch
  636. ) {
  637. //>>includeStart('debug', pragmas.debug);
  638. Check.defined("packedSamples", packedSamples);
  639. //>>includeEnd('debug');
  640. mergeNewSamples(
  641. epoch,
  642. this._times,
  643. this._values,
  644. packedSamples,
  645. this._packedLength
  646. );
  647. this._updateTableLength = true;
  648. this._definitionChanged.raiseEvent(this);
  649. };
  650. /**
  651. * Removes a sample at the given time, if present.
  652. *
  653. * @param {JulianDate} time The sample time.
  654. * @returns {boolean} <code>true</code> if a sample at time was removed, <code>false</code> otherwise.
  655. */
  656. SampledProperty.prototype.removeSample = function (time) {
  657. //>>includeStart('debug', pragmas.debug);
  658. Check.defined("time", time);
  659. //>>includeEnd('debug');
  660. const index = binarySearch(this._times, time, JulianDate.compare);
  661. if (index < 0) {
  662. return false;
  663. }
  664. removeSamples(this, index, 1);
  665. return true;
  666. };
  667. function removeSamples(property, startIndex, numberToRemove) {
  668. const packedLength = property._packedLength;
  669. property._times.splice(startIndex, numberToRemove);
  670. property._values.splice(
  671. startIndex * packedLength,
  672. numberToRemove * packedLength
  673. );
  674. property._updateTableLength = true;
  675. property._definitionChanged.raiseEvent(property);
  676. }
  677. /**
  678. * Removes all samples for the given time interval.
  679. *
  680. * @param {TimeInterval} time The time interval for which to remove all samples.
  681. */
  682. SampledProperty.prototype.removeSamples = function (timeInterval) {
  683. //>>includeStart('debug', pragmas.debug);
  684. Check.defined("timeInterval", timeInterval);
  685. //>>includeEnd('debug');
  686. const times = this._times;
  687. let startIndex = binarySearch(times, timeInterval.start, JulianDate.compare);
  688. if (startIndex < 0) {
  689. startIndex = ~startIndex;
  690. } else if (!timeInterval.isStartIncluded) {
  691. ++startIndex;
  692. }
  693. let stopIndex = binarySearch(times, timeInterval.stop, JulianDate.compare);
  694. if (stopIndex < 0) {
  695. stopIndex = ~stopIndex;
  696. } else if (timeInterval.isStopIncluded) {
  697. ++stopIndex;
  698. }
  699. removeSamples(this, startIndex, stopIndex - startIndex);
  700. };
  701. /**
  702. * Compares this property to the provided property and returns
  703. * <code>true</code> if they are equal, <code>false</code> otherwise.
  704. *
  705. * @param {Property} [other] The other property.
  706. * @returns {boolean} <code>true</code> if left and right are equal, <code>false</code> otherwise.
  707. */
  708. SampledProperty.prototype.equals = function (other) {
  709. if (this === other) {
  710. return true;
  711. }
  712. if (!defined(other)) {
  713. return false;
  714. }
  715. if (
  716. this._type !== other._type || //
  717. this._interpolationDegree !== other._interpolationDegree || //
  718. this._interpolationAlgorithm !== other._interpolationAlgorithm
  719. ) {
  720. return false;
  721. }
  722. const derivativeTypes = this._derivativeTypes;
  723. const hasDerivatives = defined(derivativeTypes);
  724. const otherDerivativeTypes = other._derivativeTypes;
  725. const otherHasDerivatives = defined(otherDerivativeTypes);
  726. if (hasDerivatives !== otherHasDerivatives) {
  727. return false;
  728. }
  729. let i;
  730. let length;
  731. if (hasDerivatives) {
  732. length = derivativeTypes.length;
  733. if (length !== otherDerivativeTypes.length) {
  734. return false;
  735. }
  736. for (i = 0; i < length; i++) {
  737. if (derivativeTypes[i] !== otherDerivativeTypes[i]) {
  738. return false;
  739. }
  740. }
  741. }
  742. const times = this._times;
  743. const otherTimes = other._times;
  744. length = times.length;
  745. if (length !== otherTimes.length) {
  746. return false;
  747. }
  748. for (i = 0; i < length; i++) {
  749. if (!JulianDate.equals(times[i], otherTimes[i])) {
  750. return false;
  751. }
  752. }
  753. const values = this._values;
  754. const otherValues = other._values;
  755. length = values.length;
  756. //Since time lengths are equal, values length and other length are guaranteed to be equal.
  757. for (i = 0; i < length; i++) {
  758. if (values[i] !== otherValues[i]) {
  759. return false;
  760. }
  761. }
  762. return true;
  763. };
  764. //Exposed for testing.
  765. SampledProperty._mergeNewSamples = mergeNewSamples;
  766. export default SampledProperty;