From 4e46d04c2a064640148c6b8f6c6533c60d1c6506 Mon Sep 17 00:00:00 2001 From: Scott Hunter Date: Mon, 8 Apr 2019 16:07:57 -0400 Subject: [PATCH] Add functions to remove samples from SampledProperty. --- Source/DataSources/SampledPositionProperty.js | 29 ++- Source/DataSources/SampledProperty.js | 92 ++++++--- .../SampledPositionPropertySpec.js | 50 ++++- Specs/DataSources/SampledPropertySpec.js | 175 +++++++++++++++++- 4 files changed, 308 insertions(+), 38 deletions(-) diff --git a/Source/DataSources/SampledPositionProperty.js b/Source/DataSources/SampledPositionProperty.js index 28da3c99fada..11f65cf42656 100644 --- a/Source/DataSources/SampledPositionProperty.js +++ b/Source/DataSources/SampledPositionProperty.js @@ -1,5 +1,6 @@ define([ '../Core/Cartesian3', + '../Core/Check', '../Core/defaultValue', '../Core/defined', '../Core/defineProperties', @@ -11,6 +12,7 @@ define([ './SampledProperty' ], function( Cartesian3, + Check, defaultValue, defined, defineProperties, @@ -210,12 +212,8 @@ define([ */ SampledPositionProperty.prototype.getValueInReferenceFrame = function(time, referenceFrame, result) { //>>includeStart('debug', pragmas.debug); - if (!defined(time)) { - throw new DeveloperError('time is required.'); - } - if (!defined(referenceFrame)) { - throw new DeveloperError('referenceFrame is required.'); - } + Check.defined('time', time); + Check.defined('referenceFrame', referenceFrame); //>>includeEnd('debug'); result = this._property.getValue(time, result); @@ -277,6 +275,25 @@ define([ this._property.addSamplesPackedArray(packedSamples, epoch); }; + /** + * Removes a sample at the given time, if present. + * + * @param {JulianDate} time The sample time. + * @returns {Boolean} true if a sample at time was removed, false otherwise. + */ + SampledPositionProperty.prototype.removeSample = function(time) { + this._property.removeSample(time); + }; + + /** + * Removes all samples for the given time interval. + * + * @param {TimeInterval} time The time interval for which to remove all samples. + */ + SampledPositionProperty.prototype.removeSamples = function(timeInterval) { + this._property.removeSamples(timeInterval); + }; + /** * Compares this property to the provided property and returns * true if they are equal, false otherwise. diff --git a/Source/DataSources/SampledProperty.js b/Source/DataSources/SampledProperty.js index 469f10815c79..b4aa7424cd44 100644 --- a/Source/DataSources/SampledProperty.js +++ b/Source/DataSources/SampledProperty.js @@ -1,5 +1,6 @@ define([ '../Core/binarySearch', + '../Core/Check', '../Core/defaultValue', '../Core/defined', '../Core/defineProperties', @@ -10,6 +11,7 @@ define([ '../Core/LinearApproximation' ], function( binarySearch, + Check, defaultValue, defined, defineProperties, @@ -166,9 +168,7 @@ define([ */ function SampledProperty(type, derivativeTypes) { //>>includeStart('debug', pragmas.debug); - if (!defined(type)) { - throw new DeveloperError('type is required.'); - } + Check.defined('type', type); //>>includeEnd('debug'); var innerType = type; @@ -372,9 +372,7 @@ define([ */ SampledProperty.prototype.getValue = function(time, result) { //>>includeStart('debug', pragmas.debug); - if (!defined(time)) { - throw new DeveloperError('time is required.'); - } + Check.defined('time', time); //>>includeEnd('debug'); var times = this._times; @@ -531,7 +529,7 @@ define([ }; /** - * Adds a new sample + * Adds a new sample. * * @param {JulianDate} time The sample time. * @param {Packable} value The value at the provided time. @@ -542,14 +540,10 @@ define([ var hasDerivatives = defined(innerDerivativeTypes); //>>includeStart('debug', pragmas.debug); - if (!defined(time)) { - throw new DeveloperError('time is required.'); - } - if (!defined(value)) { - throw new DeveloperError('value is required.'); - } - if (hasDerivatives && !defined(derivatives)) { - throw new DeveloperError('derivatives is required.'); + Check.defined('time', time); + Check.defined('value', value); + if (hasDerivatives) { + Check.defined('derivatives', derivatives); } //>>includeEnd('debug'); @@ -570,7 +564,7 @@ define([ }; /** - * Adds an array of samples + * Adds an array of samples. * * @param {JulianDate[]} times An array of JulianDate instances where each index is a sample time. * @param {Packable[]} values The array of values, where each value corresponds to the provided times index. @@ -584,12 +578,8 @@ define([ var hasDerivatives = defined(innerDerivativeTypes); //>>includeStart('debug', pragmas.debug); - if (!defined(times)) { - throw new DeveloperError('times is required.'); - } - if (!defined(values)) { - throw new DeveloperError('values is required.'); - } + Check.defined('times', times); + Check.defined('values', values); if (times.length !== values.length) { throw new DeveloperError('times and values must be the same length.'); } @@ -627,9 +617,7 @@ define([ */ SampledProperty.prototype.addSamplesPackedArray = function(packedSamples, epoch) { //>>includeStart('debug', pragmas.debug); - if (!defined(packedSamples)) { - throw new DeveloperError('packedSamples is required.'); - } + Check.defined('packedSamples', packedSamples); //>>includeEnd('debug'); mergeNewSamples(epoch, this._times, this._values, packedSamples, this._packedLength); @@ -637,6 +625,60 @@ define([ this._definitionChanged.raiseEvent(this); }; + /** + * Removes a sample at the given time, if present. + * + * @param {JulianDate} time The sample time. + * @returns {Boolean} true if a sample at time was removed, false otherwise. + */ + SampledProperty.prototype.removeSample = function(time) { + //>>includeStart('debug', pragmas.debug); + Check.defined('time', time); + //>>includeEnd('debug'); + + var index = binarySearch(this._times, time, JulianDate.compare); + if (index < 0) { + return false; + } + removeSamples(this, index, 1); + return true; + }; + + function removeSamples(property, startIndex, numberToRemove) { + var packedLength = property._packedLength; + property._times.splice(startIndex, numberToRemove); + property._values.splice(startIndex * packedLength, numberToRemove * packedLength); + property._updateTableLength = true; + property._definitionChanged.raiseEvent(property); + } + + /** + * Removes all samples for the given time interval. + * + * @param {TimeInterval} time The time interval for which to remove all samples. + */ + SampledProperty.prototype.removeSamples = function(timeInterval) { + //>>includeStart('debug', pragmas.debug); + Check.defined('timeInterval', timeInterval); + //>>includeEnd('debug'); + + var times = this._times; + var startIndex = binarySearch(times, timeInterval.start, JulianDate.compare); + if (startIndex < 0) { + startIndex = ~startIndex; + } else if (!timeInterval.isStartIncluded) { + ++startIndex; + } + var stopIndex = binarySearch(times, timeInterval.stop, JulianDate.compare); + if (stopIndex < 0) { + stopIndex = ~stopIndex; + } else if (timeInterval.isStopIncluded) { + ++stopIndex; + } + + removeSamples(this, startIndex, stopIndex - startIndex); + }; + /** * Compares this property to the provided property and returns * true if they are equal, false otherwise. diff --git a/Specs/DataSources/SampledPositionPropertySpec.js b/Specs/DataSources/SampledPositionPropertySpec.js index 90b750cdc01b..8528eef8c0fa 100644 --- a/Specs/DataSources/SampledPositionPropertySpec.js +++ b/Specs/DataSources/SampledPositionPropertySpec.js @@ -6,6 +6,7 @@ defineSuite([ 'Core/LagrangePolynomialApproximation', 'Core/LinearApproximation', 'Core/ReferenceFrame', + 'Core/TimeInterval', 'DataSources/PositionProperty' ], function( SampledPositionProperty, @@ -15,6 +16,7 @@ defineSuite([ LagrangePolynomialApproximation, LinearApproximation, ReferenceFrame, + TimeInterval, PositionProperty) { 'use strict'; @@ -110,8 +112,8 @@ defineSuite([ }); it('addSample works', function() { - var values = [new Cartesian3(7, 8, 9), new Cartesian3(8, 9, 10), new Cartesian3(9, 10, 11)]; var times = [new JulianDate(0, 0), new JulianDate(1, 0), new JulianDate(2, 0)]; + var values = [new Cartesian3(7, 8, 9), new Cartesian3(8, 9, 10), new Cartesian3(9, 10, 11)]; var property = new SampledPositionProperty(); property.addSample(times[0], values[0]); @@ -125,8 +127,8 @@ defineSuite([ }); it('addSamples works', function() { - var values = [new Cartesian3(7, 8, 9), new Cartesian3(8, 9, 10), new Cartesian3(9, 10, 11)]; var times = [new JulianDate(0, 0), new JulianDate(1, 0), new JulianDate(2, 0)]; + var values = [new Cartesian3(7, 8, 9), new Cartesian3(8, 9, 10), new Cartesian3(9, 10, 11)]; var property = new SampledPositionProperty(); property.addSamples(times, values); @@ -136,6 +138,50 @@ defineSuite([ expect(property.getValue(new JulianDate(0.5, 0))).toEqual(new Cartesian3(7.5, 8.5, 9.5)); }); + it('can remove a sample at a date', function() { + var times = [new JulianDate(0, 0), new JulianDate(1, 0), new JulianDate(2, 0)]; + var values = [new Cartesian3(7, 8, 9), new Cartesian3(18, 19, 110), new Cartesian3(9, 10, 11)]; + + var property = new SampledPositionProperty(); + property.addSamples(times, values); + + var listener = jasmine.createSpy('listener'); + property.definitionChanged.addEventListener(listener); + + property.removeSample(times[1]); + + expect(listener).toHaveBeenCalledWith(property); + + expect(property.getValue(times[0])).toEqual(values[0]); + // removing the sample at times[1] causes the property to interpolate + expect(property.getValue(times[1])).toEqual(new Cartesian3(8, 9, 10)); + expect(property.getValue(times[2])).toEqual(values[2]); + }); + + it('can remove samples for a time interval', function() { + var times = [new JulianDate(0, 0), new JulianDate(1, 0), new JulianDate(2, 0), new JulianDate(3, 0)]; + var values = [new Cartesian3(7, 8, 9), new Cartesian3(18, 19, 110), new Cartesian3(19, 20, 110), new Cartesian3(10, 11, 12)]; + + var property = new SampledPositionProperty(); + property.addSamples(times, values); + + var listener = jasmine.createSpy('listener'); + property.definitionChanged.addEventListener(listener); + + property.removeSamples(new TimeInterval({ + start: times[1], + stop: times[2] + })); + + expect(listener).toHaveBeenCalledWith(property); + + expect(property.getValue(times[0])).toEqual(values[0]); + // removing the samples causes the property to interpolate + expect(property.getValue(times[1])).toEqual(new Cartesian3(8, 9, 10)); + expect(property.getValue(times[2])).toEqual(new Cartesian3(9, 10, 11)); + expect(property.getValue(times[3])).toEqual(values[3]); + }); + it('addSamplesPackedArray works with derivatives', function() { var data = [0, 7, 8, 9, 1, 0, 0, 1, 8, 9, 10, 0, 1, 0, 2, 9, 10, 11, 0, 0, 1]; var epoch = new JulianDate(0, 0); diff --git a/Specs/DataSources/SampledPropertySpec.js b/Specs/DataSources/SampledPropertySpec.js index 06617facf84d..763d123cced9 100644 --- a/Specs/DataSources/SampledPropertySpec.js +++ b/Specs/DataSources/SampledPropertySpec.js @@ -8,7 +8,8 @@ defineSuite([ 'Core/LagrangePolynomialApproximation', 'Core/LinearApproximation', 'Core/Math', - 'Core/Quaternion' + 'Core/Quaternion', + 'Core/TimeInterval' ], function( SampledProperty, Cartesian3, @@ -19,7 +20,8 @@ defineSuite([ LagrangePolynomialApproximation, LinearApproximation, CesiumMath, - Quaternion) { + Quaternion, + TimeInterval) { 'use strict'; it('constructor sets expected defaults', function() { @@ -70,8 +72,8 @@ defineSuite([ }); it('addSample works', function() { - var values = [7, 8, 9]; var times = [new JulianDate(0, 0), new JulianDate(1, 0), new JulianDate(2, 0)]; + var values = [7, 8, 9]; var property = new SampledProperty(Number); var listener = jasmine.createSpy('listener'); @@ -96,8 +98,8 @@ defineSuite([ }); it('addSamples works', function() { - var values = [7, 8, 9]; var times = [new JulianDate(0, 0), new JulianDate(1, 0), new JulianDate(2, 0)]; + var values = [7, 8, 9]; var property = new SampledProperty(Number); var listener = jasmine.createSpy('listener'); @@ -111,6 +113,169 @@ defineSuite([ expect(property.getValue(new JulianDate(0.5, 0))).toEqual(7.5); }); + it('can remove a sample at a date', function() { + var times = [new JulianDate(0, 0), new JulianDate(1, 0), new JulianDate(2, 0)]; + var values = [1, 8, 3]; + + var property = new SampledProperty(Number); + property.addSamples(times, values); + expect(property.getValue(times[0])).toEqual(values[0]); + expect(property.getValue(times[1])).toEqual(values[1]); + expect(property.getValue(times[2])).toEqual(values[2]); + + var listener = jasmine.createSpy('listener'); + property.definitionChanged.addEventListener(listener); + + property.removeSample(times[1]); + + expect(listener).toHaveBeenCalledWith(property); + + expect(property._times.length).toEqual(2); + expect(property._values.length).toEqual(2); + + expect(property.getValue(times[0])).toEqual(values[0]); + // by deleting the sample at times[1] we now linearly interpolate from the remaining samples + expect(property.getValue(times[1])).toEqual((values[0] + values[2]) / 2); + expect(property.getValue(times[2])).toEqual(values[2]); + }); + + function arraySubset(array, startIndex, count) { + array = array.slice(); + array.splice(startIndex, count); + return array; + } + + it('can remove samples for a time interval', function() { + var times = [new JulianDate(0, 0), new JulianDate(1, 0), new JulianDate(2, 0), new JulianDate(3, 0), new JulianDate(4, 0)]; + var values = [1, 8, 13, 1, 3]; + + function createProperty() { + var property = new SampledProperty(Number); + property.addSamples(times, values); + expect(property.getValue(times[0])).toEqual(values[0]); + expect(property.getValue(times[1])).toEqual(values[1]); + expect(property.getValue(times[2])).toEqual(values[2]); + expect(property.getValue(times[3])).toEqual(values[3]); + expect(property.getValue(times[4])).toEqual(values[4]); + return property; + } + + var property = createProperty(); + var listener = jasmine.createSpy('listener'); + property.definitionChanged.addEventListener(listener); + + property.removeSamples(new TimeInterval({ + start: times[1], + stop: times[3] + })); + + expect(listener).toHaveBeenCalledWith(property); + expect(property._times.length).toEqual(2); + expect(property._values.length).toEqual(2); + expect(property._times).toEqual(arraySubset(times, 1, 3)); + expect(property._values).toEqual(arraySubset(values, 1, 3)); + + expect(property.getValue(times[0])).toEqual(values[0]); + // by deleting the samples we now linearly interpolate from the remaining samples + expect(property.getValue(times[2])).toEqual((values[0] + values[4]) / 2); + expect(property.getValue(times[4])).toEqual(values[4]); + + property = createProperty(); + listener = jasmine.createSpy('listener'); + property.definitionChanged.addEventListener(listener); + + // remove using a start time just after a sample + property.removeSamples(new TimeInterval({ + start: JulianDate.addSeconds(times[1], 4, new JulianDate()), + stop: times[3] + })); + + expect(listener).toHaveBeenCalledWith(property); + expect(property._times.length).toEqual(3); + expect(property._values.length).toEqual(3); + expect(property._times).toEqual(arraySubset(times, 2, 2)); + expect(property._values).toEqual(arraySubset(values, 2, 2)); + + property = createProperty(); + listener = jasmine.createSpy('listener'); + property.definitionChanged.addEventListener(listener); + + // remove using a stop time just before a sample + property.removeSamples(new TimeInterval({ + start: JulianDate.addSeconds(times[1], 4, new JulianDate()), + stop: JulianDate.addSeconds(times[3], -4, new JulianDate()) + })); + + expect(listener).toHaveBeenCalledWith(property); + expect(property._times.length).toEqual(4); + expect(property._values.length).toEqual(4); + expect(property._times).toEqual(arraySubset(times, 2, 1)); + expect(property._values).toEqual(arraySubset(values, 2, 1)); + }); + + it('can remove samples for a time interval with start or stop not included', function() { + var times = [new JulianDate(0, 0), new JulianDate(1, 0), new JulianDate(2, 0), new JulianDate(3, 0), new JulianDate(4, 0)]; + var values = [1, 8, 13, 1, 3]; + + function createProperty() { + var property = new SampledProperty(Number); + property.addSamples(times, values); + expect(property.getValue(times[0])).toEqual(values[0]); + expect(property.getValue(times[1])).toEqual(values[1]); + expect(property.getValue(times[2])).toEqual(values[2]); + expect(property.getValue(times[3])).toEqual(values[3]); + expect(property.getValue(times[4])).toEqual(values[4]); + return property; + } + + var property = createProperty(); + var listener = jasmine.createSpy('listener'); + property.definitionChanged.addEventListener(listener); + + property.removeSamples(new TimeInterval({ + start: times[1], + stop: times[3], + isStartIncluded: false, + isStopIncluded: true + })); + + expect(listener).toHaveBeenCalledWith(property); + expect(property._times).toEqual(arraySubset(times, 2, 2)); + expect(property._values).toEqual(arraySubset(values, 2, 2)); + + property = createProperty(); + + listener = jasmine.createSpy('listener'); + property.definitionChanged.addEventListener(listener); + + property.removeSamples(new TimeInterval({ + start: times[1], + stop: times[3], + isStartIncluded: true, + isStopIncluded: false + })); + + expect(listener).toHaveBeenCalledWith(property); + expect(property._times).toEqual(arraySubset(times, 1, 2)); + expect(property._values).toEqual(arraySubset(values, 1, 2)); + + property = createProperty(); + + listener = jasmine.createSpy('listener'); + property.definitionChanged.addEventListener(listener); + + property.removeSamples(new TimeInterval({ + start: times[1], + stop: times[3], + isStartIncluded: false, + isStopIncluded: false + })); + + expect(listener).toHaveBeenCalledWith(property); + expect(property._times).toEqual(arraySubset(times, 2, 1)); + expect(property._values).toEqual(arraySubset(values, 2, 1)); + }); + it('works with PackableForInterpolation', function() { function CustomType(value) { this.x = value; @@ -143,8 +308,8 @@ defineSuite([ return result; }; - var values = [new CustomType(0), new CustomType(2), new CustomType(4)]; var times = [new JulianDate(0, 0), new JulianDate(1, 0), new JulianDate(2, 0)]; + var values = [new CustomType(0), new CustomType(2), new CustomType(4)]; var property = new SampledProperty(CustomType); property.addSample(times[0], values[0]);